mirror of
https://github.com/flibusta-apps/book_bot.git
synced 2025-12-06 15:35:35 +01:00
Add allowed lang settings
This commit is contained in:
@@ -10,3 +10,7 @@ export const SEQUENCE_BOOKS_PREFIX = 'bs_';
|
||||
export const RANDOM_BOOK = 'random_book';
|
||||
export const RANDOM_AUTHOR = 'random_author';
|
||||
export const RANDOM_SEQUENCE = 'random_sequence';
|
||||
|
||||
export const LANG_SETTINGS = 'lang_settings';
|
||||
export const ENABLE_LANG_PREFIX = 'lang_on_';
|
||||
export const DISABLE_LANG_PREFIX = 'lang_off_';
|
||||
|
||||
@@ -12,10 +12,10 @@ import * as BookLibrary from "./services/book_library";
|
||||
import { CachedMessage, getBookCache } from './services/book_cache';
|
||||
import { getBookCacheBuffer } from './services/book_cache_buffer';
|
||||
import { download } from './services/downloader';
|
||||
import { createOrUpdateUserSettings } from './services/user_settings';
|
||||
import { createOrUpdateUserSettings, getUserSettings } from './services/user_settings';
|
||||
import { formatBook, formatAuthor, formatSequence } from './format';
|
||||
import { getPaginatedMessage, registerPaginationCommand, registerRandomItemCallback } from './utils';
|
||||
import { getRandomKeyboard } from './keyboard';
|
||||
import { getPaginatedMessage, registerLanguageSettingsCallback, registerPaginationCommand, registerRandomItemCallback } from './utils';
|
||||
import { getRandomKeyboard, getUserAllowedLangsKeyboard } from './keyboard';
|
||||
|
||||
|
||||
export async function createApprovedBot(token: string, state: BotState): Promise<Telegraf> {
|
||||
@@ -88,6 +88,29 @@ export async function createApprovedBot(token: string, state: BotState): Promise
|
||||
registerRandomItemCallback(bot, CallbackData.RANDOM_AUTHOR, BookLibrary.getRandomAuthor, formatAuthor);
|
||||
registerRandomItemCallback(bot, CallbackData.RANDOM_SEQUENCE, BookLibrary.getRandomSequence, formatSequence);
|
||||
|
||||
bot.command("settings", async (ctx: Context) => {
|
||||
const keyboard = Markup.inlineKeyboard([
|
||||
[Markup.button.callback("Языки", CallbackData.LANG_SETTINGS)]
|
||||
]);
|
||||
|
||||
ctx.reply("Настройки:", {
|
||||
reply_markup: keyboard.reply_markup
|
||||
});
|
||||
});
|
||||
|
||||
bot.action(CallbackData.LANG_SETTINGS, async (ctx: Context) => {
|
||||
if (!ctx.callbackQuery || !('data' in ctx.callbackQuery)) return;
|
||||
|
||||
const keyboard = await getUserAllowedLangsKeyboard(ctx.callbackQuery.from.id);
|
||||
|
||||
ctx.editMessageText("Настройки языков:", {
|
||||
reply_markup: keyboard.reply_markup,
|
||||
});
|
||||
});
|
||||
|
||||
registerLanguageSettingsCallback(bot, 'on', CallbackData.ENABLE_LANG_PREFIX);
|
||||
registerLanguageSettingsCallback(bot, 'off', CallbackData.DISABLE_LANG_PREFIX);
|
||||
|
||||
bot.hears(/^\/d_[a-zA-Z0-9]+_[\d]+$/gm, async (ctx: Context) => {
|
||||
if (!ctx.message || !('text' in ctx.message)) {
|
||||
return;
|
||||
@@ -134,7 +157,10 @@ export async function createApprovedBot(token: string, state: BotState): Promise
|
||||
|
||||
const authorId = ctx.message.text.split('_')[1];
|
||||
|
||||
const pMessage = await getPaginatedMessage(CallbackData.AUTHOR_BOOKS_PREFIX, authorId, 1, BookLibrary.getAuthorBooks, formatBook);
|
||||
const userSettings = await getUserSettings(ctx.message.from.id);
|
||||
const allowedLangs = userSettings.allowed_langs.map((lang) => lang.code);
|
||||
|
||||
const pMessage = await getPaginatedMessage(CallbackData.AUTHOR_BOOKS_PREFIX, authorId, 1, allowedLangs, BookLibrary.getAuthorBooks, formatBook);
|
||||
|
||||
await ctx.reply(pMessage.message, {
|
||||
reply_markup: pMessage.keyboard.reply_markup
|
||||
@@ -148,7 +174,10 @@ export async function createApprovedBot(token: string, state: BotState): Promise
|
||||
|
||||
const sequenceId = ctx.message.text.split('_')[1];
|
||||
|
||||
const pMessage = await getPaginatedMessage(CallbackData.SEQUENCE_BOOKS_PREFIX, sequenceId, 1, BookLibrary.getSequenceBooks, formatBook);
|
||||
const userSettings = await getUserSettings(ctx.message.from.id);
|
||||
const allowedLangs = userSettings.allowed_langs.map((lang) => lang.code);
|
||||
|
||||
const pMessage = await getPaginatedMessage(CallbackData.SEQUENCE_BOOKS_PREFIX, sequenceId, 1, allowedLangs, BookLibrary.getSequenceBooks, formatBook);
|
||||
|
||||
await ctx.reply(pMessage.message, {
|
||||
reply_markup: pMessage.keyboard.reply_markup
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Markup } from 'telegraf';
|
||||
import { InlineKeyboardMarkup } from 'typegram';
|
||||
|
||||
import { RANDOM_BOOK, RANDOM_AUTHOR, RANDOM_SEQUENCE } from './callback_data';
|
||||
import { RANDOM_BOOK, RANDOM_AUTHOR, RANDOM_SEQUENCE, ENABLE_LANG_PREFIX, DISABLE_LANG_PREFIX } from './callback_data';
|
||||
import { getUserSettings, getLanguages } from './services/user_settings';
|
||||
|
||||
|
||||
export function getPaginationKeyboard(prefix: string, query: string, page: number, totalPages: number): Markup.Markup<InlineKeyboardMarkup> {
|
||||
@@ -41,3 +42,33 @@ export function getRandomKeyboard(): Markup.Markup<InlineKeyboardMarkup> {
|
||||
[Markup.button.callback('Серию', RANDOM_SEQUENCE)],
|
||||
]);
|
||||
}
|
||||
|
||||
const DEFAULT_ALLOWED_LANGS_CODES = ['ru', 'be', 'uk'];
|
||||
|
||||
export async function getUserAllowedLangsKeyboard(userId: number): Promise<Markup.Markup<InlineKeyboardMarkup>> {
|
||||
const allLangs = await getLanguages();
|
||||
const userSettings = await getUserSettings(userId);
|
||||
|
||||
const userAllowedLangsCodes = userSettings !== null
|
||||
? userSettings.allowed_langs.map((lang) => lang.code)
|
||||
: DEFAULT_ALLOWED_LANGS_CODES;
|
||||
|
||||
const onEmoji = '🟢';
|
||||
const offEmoji = '🔴';
|
||||
|
||||
return Markup.inlineKeyboard([
|
||||
...allLangs.map((lang) => {
|
||||
let titlePrefix: string;
|
||||
let callbackDataPrefix: string;
|
||||
if (userAllowedLangsCodes.includes(lang.code)) {
|
||||
titlePrefix = onEmoji;
|
||||
callbackDataPrefix = DISABLE_LANG_PREFIX;
|
||||
} else {
|
||||
titlePrefix = offEmoji;
|
||||
callbackDataPrefix = ENABLE_LANG_PREFIX;
|
||||
}
|
||||
const title = `${titlePrefix} ${lang.label}`;
|
||||
return [Markup.button.callback(title, `${callbackDataPrefix}${lang.code}`)];
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import got from 'got';
|
||||
|
||||
import env from '@/config';
|
||||
import { getAllowedLangsSearchParams } from '../utils';
|
||||
|
||||
|
||||
const PAGE_SIZE = 7;
|
||||
@@ -94,27 +95,30 @@ export async function getBookById(book_id: number): Promise<DetailBook> {
|
||||
}
|
||||
|
||||
|
||||
export async function searchByBookName(query: string, page: number): Promise<Page<Book>> {
|
||||
return _makeRequest<Page<Book>>(`/api/v1/books/search/${query}`, {
|
||||
page: page,
|
||||
size: PAGE_SIZE,
|
||||
})
|
||||
export async function searchByBookName(query: string, page: number, allowedLangs: string[]): Promise<Page<Book>> {
|
||||
const searchParams = getAllowedLangsSearchParams(allowedLangs);
|
||||
searchParams.append('page', page.toString());
|
||||
searchParams.append('size', PAGE_SIZE.toString());
|
||||
|
||||
return _makeRequest<Page<Book>>(`/api/v1/books/search/${query}`, searchParams);
|
||||
}
|
||||
|
||||
|
||||
export async function searchAuthors(query: string, page: number): Promise<Page<Author>> {
|
||||
return _makeRequest<Page<Author>>(`/api/v1/authors/search/${query}`, {
|
||||
page: page,
|
||||
size: PAGE_SIZE,
|
||||
});
|
||||
export async function searchAuthors(query: string, page: number, allowedLangs: string[]): Promise<Page<Author>> {
|
||||
const searchParams = getAllowedLangsSearchParams(allowedLangs);
|
||||
searchParams.append('page', page.toString());
|
||||
searchParams.append('size', PAGE_SIZE.toString());
|
||||
|
||||
return _makeRequest<Page<Author>>(`/api/v1/authors/search/${query}`, searchParams);
|
||||
}
|
||||
|
||||
|
||||
export async function searchSequences(query: string, page: number): Promise<Page<Sequence>> {
|
||||
return _makeRequest<Page<Sequence>>(`/api/v1/sequences/search/${query}`, {
|
||||
page: page,
|
||||
size: PAGE_SIZE,
|
||||
});
|
||||
export async function searchSequences(query: string, page: number, allowedLangs: string[]): Promise<Page<Sequence>> {
|
||||
const searchParams = getAllowedLangsSearchParams(allowedLangs);
|
||||
searchParams.append('page', page.toString());
|
||||
searchParams.append('size', PAGE_SIZE.toString());
|
||||
|
||||
return _makeRequest<Page<Sequence>>(`/api/v1/sequences/search/${query}`, searchParams);
|
||||
}
|
||||
|
||||
|
||||
@@ -123,29 +127,31 @@ export async function getBookAnnotation(bookId: number): Promise<BookAnnotation>
|
||||
}
|
||||
|
||||
|
||||
export async function getAuthorBooks(authorId: number, page: number): Promise<Page<AuthorBook>> {
|
||||
return _makeRequest<Page<AuthorBook>>(`/api/v1/authors/${authorId}/books`, {
|
||||
page: page,
|
||||
size: PAGE_SIZE,
|
||||
});
|
||||
export async function getAuthorBooks(authorId: number, page: number, allowedLangs: string[]): Promise<Page<AuthorBook>> {
|
||||
const searchParams = getAllowedLangsSearchParams(allowedLangs);
|
||||
searchParams.append('page', page.toString());
|
||||
searchParams.append('size', PAGE_SIZE.toString());
|
||||
|
||||
return _makeRequest<Page<AuthorBook>>(`/api/v1/authors/${authorId}/books`, searchParams);
|
||||
}
|
||||
|
||||
|
||||
export async function getSequenceBooks(sequenceId: number, page: number): Promise<Page<Book>> {
|
||||
return _makeRequest<Page<Book>>(`/api/v1/sequences/${sequenceId}/books`, {
|
||||
page: page,
|
||||
size: PAGE_SIZE,
|
||||
});
|
||||
export async function getSequenceBooks(sequenceId: number, page: number, allowedLangs: string[]): Promise<Page<Book>> {
|
||||
const searchParams = getAllowedLangsSearchParams(allowedLangs);
|
||||
searchParams.append('page', page.toString());
|
||||
searchParams.append('size', PAGE_SIZE.toString());
|
||||
|
||||
return _makeRequest<Page<Book>>(`/api/v1/sequences/${sequenceId}/books`, searchParams);
|
||||
}
|
||||
|
||||
export async function getRandomBook(): Promise<Book> {
|
||||
return _makeRequest<Book>('/api/v1/books/random');
|
||||
export async function getRandomBook(allowedLangs: string[]): Promise<Book> {
|
||||
return _makeRequest<Book>('/api/v1/books/random', getAllowedLangsSearchParams(allowedLangs));
|
||||
}
|
||||
|
||||
export async function getRandomAuthor(): Promise<Author> {
|
||||
return _makeRequest<Author>('/api/v1/authors/random');
|
||||
export async function getRandomAuthor(allowedLangs: string[]): Promise<Author> {
|
||||
return _makeRequest<Author>('/api/v1/authors/random', getAllowedLangsSearchParams(allowedLangs));
|
||||
}
|
||||
|
||||
export async function getRandomSequence(): Promise<Sequence> {
|
||||
return _makeRequest<Sequence>('/api/v1/sequences/random');
|
||||
export async function getRandomSequence(allowedLangs: string[]): Promise<Sequence> {
|
||||
return _makeRequest<Sequence>('/api/v1/sequences/random', getAllowedLangsSearchParams(allowedLangs));
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface UserSettingsUpdateData {
|
||||
first_name: string;
|
||||
username: string;
|
||||
source: string;
|
||||
allowed_langs?: string[];
|
||||
}
|
||||
|
||||
|
||||
@@ -46,8 +47,8 @@ export async function getLanguages(): Promise<Language[]> {
|
||||
}
|
||||
|
||||
|
||||
export async function getUserSettings(user_id: number): Promise<UserSettings | null> {
|
||||
return _makeGetRequest<UserSettings>(`/users/${user_id}`);
|
||||
export async function getUserSettings(userId: number): Promise<UserSettings> {
|
||||
return _makeGetRequest<UserSettings>(`/users/${userId}`);
|
||||
}
|
||||
|
||||
export async function createOrUpdateUserSettings(data: UserSettingsUpdateData): Promise<UserSettings> {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Context, Markup, Telegraf, TelegramError } from 'telegraf';
|
||||
import { InlineKeyboardMarkup } from 'typegram';
|
||||
import { URLSearchParams } from 'url';
|
||||
|
||||
import { getPaginationKeyboard } from './keyboard';
|
||||
import { getPaginationKeyboard, getUserAllowedLangsKeyboard } from './keyboard';
|
||||
import * as BookLibrary from "./services/book_library";
|
||||
import { createOrUpdateUserSettings, getUserSettings } from './services/user_settings';
|
||||
|
||||
|
||||
interface PreparedMessage {
|
||||
@@ -15,10 +17,11 @@ export async function getPaginatedMessage<T>(
|
||||
prefix: string,
|
||||
data: any,
|
||||
page: number,
|
||||
itemsGetter: (data: any, page: number) => Promise<BookLibrary.Page<T>>,
|
||||
allowedLangs: string[],
|
||||
itemsGetter: (data: any, page: number, allowedLangs: string[]) => Promise<BookLibrary.Page<T>>,
|
||||
itemFormater: (item: T) => string,
|
||||
): Promise<PreparedMessage> {
|
||||
const itemsPage = await itemsGetter(data, page);
|
||||
const itemsPage = await itemsGetter(data, page, allowedLangs);
|
||||
|
||||
const formatedItems = itemsPage.items.map(itemFormater).join('\n\n\n');
|
||||
const message = formatedItems + `\n\nСтраница ${page}/${itemsPage.total_pages}`;
|
||||
@@ -34,7 +37,7 @@ export async function getPaginatedMessage<T>(
|
||||
export function registerPaginationCommand<T>(
|
||||
bot: Telegraf,
|
||||
prefix: string,
|
||||
itemsGetter: (data: any, page: number) => Promise<BookLibrary.Page<T>>,
|
||||
itemsGetter: (data: any, page: number, allowedLangs: string[]) => Promise<BookLibrary.Page<T>>,
|
||||
itemFormater: (item: T) => string,
|
||||
) {
|
||||
bot.action(new RegExp(prefix), async (ctx: Context) => {
|
||||
@@ -42,7 +45,10 @@ export function registerPaginationCommand<T>(
|
||||
|
||||
const [_, query, sPage] = ctx.callbackQuery.data.split('_');
|
||||
|
||||
const pMessage = await getPaginatedMessage(prefix, query, parseInt(sPage), itemsGetter, itemFormater);
|
||||
const userSettings = await getUserSettings(ctx.callbackQuery.from.id);
|
||||
const allowedLangs = userSettings.allowed_langs.map((lang) => lang.code);
|
||||
|
||||
const pMessage = await getPaginatedMessage(prefix, query, parseInt(sPage), allowedLangs, itemsGetter, itemFormater);
|
||||
|
||||
try {
|
||||
await ctx.editMessageText(pMessage.message, {
|
||||
@@ -57,11 +63,17 @@ export function registerPaginationCommand<T>(
|
||||
export function registerRandomItemCallback<T>(
|
||||
bot: Telegraf,
|
||||
callback_data: string,
|
||||
itemGetter: () => Promise<T>,
|
||||
itemGetter: (allowedLangs: string[]) => Promise<T>,
|
||||
itemFormatter: (item: T) => string,
|
||||
) {
|
||||
bot.action(callback_data, async (ctx: Context) => {
|
||||
const item = await itemGetter();
|
||||
if (!ctx.callbackQuery || !('data' in ctx.callbackQuery)) return;
|
||||
|
||||
const userSettings = await getUserSettings(ctx.callbackQuery.from.id);
|
||||
|
||||
const item = await itemGetter(
|
||||
userSettings.allowed_langs.map((lang) => lang.code)
|
||||
);
|
||||
|
||||
const keyboard = Markup.inlineKeyboard([
|
||||
[Markup.button.callback("Повторить?", callback_data)]
|
||||
@@ -74,3 +86,53 @@ export function registerRandomItemCallback<T>(
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function registerLanguageSettingsCallback(
|
||||
bot: Telegraf,
|
||||
action: 'on' | 'off',
|
||||
prefix: string,
|
||||
) {
|
||||
bot.action(new RegExp(prefix), async (ctx: Context) => {
|
||||
if (!ctx.callbackQuery || !('data' in ctx.callbackQuery)) return;
|
||||
|
||||
const userSettings = await getUserSettings(ctx.callbackQuery.from.id);
|
||||
|
||||
let allowedLangsCodes = userSettings.allowed_langs.map((item) => item.code);
|
||||
|
||||
const tLang = ctx.callbackQuery.data.split("_")[2];
|
||||
|
||||
if (action === 'on') {
|
||||
allowedLangsCodes.push(tLang);
|
||||
} else {
|
||||
allowedLangsCodes = allowedLangsCodes.filter((item) => item !== tLang);
|
||||
}
|
||||
|
||||
if (allowedLangsCodes.length === 0) {
|
||||
ctx.answerCbQuery("Должен быть активен, хотя бы один язык!", {
|
||||
show_alert: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const user = ctx.callbackQuery.from;
|
||||
await createOrUpdateUserSettings({
|
||||
user_id: user.id,
|
||||
last_name: user.last_name || '',
|
||||
first_name: user.first_name,
|
||||
username: user.username || '',
|
||||
source: ctx.botInfo.username,
|
||||
allowed_langs: allowedLangsCodes,
|
||||
});
|
||||
|
||||
const keyboard = await getUserAllowedLangsKeyboard(user.id);
|
||||
|
||||
ctx.editMessageReplyMarkup(keyboard.reply_markup);
|
||||
});
|
||||
}
|
||||
|
||||
export function getAllowedLangsSearchParams(allowedLangs: string[]): URLSearchParams {
|
||||
const sp = new URLSearchParams();
|
||||
allowedLangs.forEach((lang) => sp.append('allowed_langs', lang));
|
||||
return sp;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import express, { Response, Request, NextFunction } from 'express';
|
||||
import { Server } from 'http';
|
||||
|
||||
import * as dockerIpTools from "docker-ip-get";
|
||||
|
||||
import got from 'got';
|
||||
|
||||
@@ -6,7 +9,6 @@ import { Telegraf } from 'telegraf';
|
||||
|
||||
import env from '@/config';
|
||||
import getBot, { BotStatuses } from './factory/index';
|
||||
import { Server } from 'http';
|
||||
|
||||
export enum Cache {
|
||||
ORIGINAL = "original",
|
||||
@@ -84,8 +86,12 @@ export default class BotsManager {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
const dockerIp = await dockerIpTools.getContainerIp();
|
||||
|
||||
await bot.telegram.setWebhook(
|
||||
`${env.WEBHOOK_BASE_URL}:${env.WEBHOOK_PORT}/${state.id}/${bot.telegram.token}`
|
||||
`${env.WEBHOOK_BASE_URL}:${env.WEBHOOK_PORT}/${state.id}/${bot.telegram.token}`, {
|
||||
ip_address: dockerIp,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import BotsManager from './bots/manager';
|
||||
|
||||
setTimeout(() => BotsManager.start(), 5000);
|
||||
setTimeout(() => BotsManager.start(), 5_000);
|
||||
|
||||
Reference in New Issue
Block a user