bing-chat-telegram/src/fmt-replace.ts
Udit Karode 77c704db49
init
2023-04-08 10:17:49 +05:30

130 lines
3.9 KiB
TypeScript

// This code is licensed under the MIT License
// Copyright (c) MKRhere (https://mkr.pw)
import { FmtString } from "telegraf/format";
import { MessageEntity } from "typegram";
interface EntityCompare {
offset: number;
length: number;
}
/** get the starting of the entity */
const starts = (e: EntityCompare) => e.offset;
/** get the ending of the entity */
const ends = (e: EntityCompare) => e.offset + e.length;
const before = (A: EntityCompare, B: EntityCompare) =>
// B ends before A starts
ends(B) <= starts(A);
const after = (A: EntityCompare, B: EntityCompare) =>
// B starts after A ends
starts(B) >= ends(A);
const inside = (A: EntityCompare, B: EntityCompare) =>
// B starts with/after A and ends before A
(starts(B) >= starts(A) && ends(B) < ends(A)) ||
// B starts after A and ends before/with A
(starts(B) > starts(A) && ends(B) <= ends(A));
const contains = (A: EntityCompare, B: EntityCompare) =>
// B starts before/with A and ends with/after A
starts(B) <= starts(A) && ends(B) >= ends(A);
const endsInside = (A: EntityCompare, B: EntityCompare) =>
// B starts before A starts, ends after A starts, ends before B ends
starts(B) < starts(A) && ends(B) > starts(A) && ends(B) < ends(A);
const startsInside = (A: EntityCompare, B: EntityCompare) =>
// B starts after A, starts before A ends, ends after A
starts(B) > starts(A) && starts(B) < ends(A) && ends(B) > ends(A);
export const replace = (
source: string | FmtString,
search: string | RegExp,
value: string | FmtString | ((...match: string[]) => string | FmtString),
): FmtString => {
source = FmtString.normalise(source);
let text = source.text;
let entities: MessageEntity[] | undefined = source.entities;
function fixEntities(offset: number, length: number, correction: number) {
const A = { offset, length };
return (entities || [])
.map(E => {
if (before(A, E)) return E;
if (inside(A, E)) return;
if (after(A, E)) return { ...E, offset: E.offset + correction };
if (contains(A, E)) return { ...E, length: E.length + correction };
if (endsInside(A, E))
return { ...E, length: E.length - (ends(E) - starts(A)) };
if (startsInside(A, E)) {
const entityInside = ends(A) - starts(E);
return {
...E,
offset: E.offset + entityInside + correction,
length: E.length - entityInside,
};
}
throw new Error(
"Entity found in an unexpected condition. " +
"This is probably a bug in telegraf. " +
"You should report this to https://github.com/telegraf/telegraf/issues",
);
})
.filter((x): x is MessageEntity => Boolean(x));
}
if (typeof search === "string") {
const replace = FmtString.normalise(
typeof value === "function" ? value(...search) : value,
);
const offset = text.indexOf(search);
const length = search.length;
text = text.slice(0, offset) + replace.text + text.slice(offset + length);
const currentCorrection = replace.text.length - length;
entities = [
...fixEntities(offset, length, currentCorrection),
...(replace.entities || []).map(E => ({
...E,
offset: E.offset + offset,
})),
];
} else {
let index = 0; // context position in text string
let acc = ""; // incremental return value
let correction = 0;
let regexArray: RegExpExecArray | null;
while ((regexArray = search.exec(text))) {
const match = regexArray[0];
const offset = regexArray.index;
const length = match.length;
const replace = FmtString.normalise(
typeof value === "function" ? value(...regexArray) : value,
);
acc += text.slice(index, offset) + replace.text;
const currentCorrection = replace.text.length - length;
entities = [
...fixEntities(offset + correction, length, currentCorrection),
...(replace.entities || []).map(E => ({
...E,
offset: E.offset + offset + correction,
})),
];
correction += currentCorrection;
index = offset + length;
}
text = acc + text.slice(index);
}
return new FmtString(text, entities);
};