From 707e317e16ffa3c311dc5b6348c4c6d214a913e5 Mon Sep 17 00:00:00 2001 From: teidesu Date: Sat, 12 Jun 2021 01:13:02 +0300 Subject: [PATCH] stuff --- packages/client/src/types/bots/keyboards.ts | 42 +++++++ packages/client/src/types/peers/chat.ts | 27 ++++ packages/client/src/types/peers/user.ts | 27 ++++ packages/dispatcher/src/filters.ts | 131 ++++++++++++++++++-- packages/dispatcher/src/propagation.ts | 9 +- 5 files changed, 222 insertions(+), 14 deletions(-) diff --git a/packages/client/src/types/bots/keyboards.ts b/packages/client/src/types/bots/keyboards.ts index 3d7fd0a7..5a229f50 100644 --- a/packages/client/src/types/bots/keyboards.ts +++ b/packages/client/src/types/bots/keyboards.ts @@ -263,6 +263,20 @@ export namespace BotKeyboard { return { _: 'keyboardButtonGame', text } } + /** + * Button to pay for a product. + * + * Used for inline keyboards, not reply! + * + * **Note**: This type of button must always be + * the first button in the first row. Related + * invoice is inferred from {@link InputMedia.invoice}, + * thus this button should only be used with it. + */ + export function pay(text: string): tl.RawKeyboardButtonBuy { + return { _: 'keyboardButtonBuy', text } + } + /** * Button to authorize a user * @@ -309,6 +323,34 @@ export namespace BotKeyboard { } } + /** + * Find a button in the keyboard by its text or by predicate + * + * @param buttons Two-dimensional array of buttons + * @param predicate Button text or predicate function + */ + export function findButton( + buttons: tl.TypeKeyboardButton[][], + predicate: string | ((btn: tl.TypeKeyboardButton) => boolean) + ): tl.TypeKeyboardButton | null { + if (typeof predicate === 'string') { + const text = predicate + predicate = (btn) => { + return 'text' in btn && btn.text === text + } + } + + for (const row of buttons) { + for (const btn of row) { + if (predicate(btn)) { + return btn + } + } + } + + return null + } + /** @internal */ export function _rowsTo2d( rows: tl.RawKeyboardButtonRow[] diff --git a/packages/client/src/types/peers/chat.ts b/packages/client/src/types/peers/chat.ts index 7280295c..92257e52 100644 --- a/packages/client/src/types/peers/chat.ts +++ b/packages/client/src/types/peers/chat.ts @@ -7,6 +7,7 @@ import { MtCuteArgumentError, MtCuteTypeAssertionError } from '../errors' import { makeInspectable } from '../utils' import { ChatsIndex, InputPeerLike, User, UsersIndex } from './index' import { ChatLocation } from './chat-location' +import { InputMediaLike } from '../media' export namespace Chat { /** @@ -559,6 +560,32 @@ export class Chat { async readHistory(message = 0, clearMentions = false): Promise { return this.client.readHistory(this.inputPeer, message, clearMentions) } + + /** + * Send a text message in this chat. + * + * @param text Text of the message + * @param params + */ + sendText( + text: string, + params?: Parameters[2] + ): ReturnType { + return this.client.sendText(this.inputPeer, text, params) + } + + /** + * Send a media in this chat. + * + * @param media Media to send + * @param params + */ + sendMedia( + media: InputMediaLike, + params?: Parameters[2] + ): ReturnType { + return this.client.sendMedia(this.inputPeer, media, params) + } } makeInspectable(Chat, [], ['user']) diff --git a/packages/client/src/types/peers/user.ts b/packages/client/src/types/peers/user.ts index 352a7755..8e081a6c 100644 --- a/packages/client/src/types/peers/user.ts +++ b/packages/client/src/types/peers/user.ts @@ -4,6 +4,7 @@ import { ChatPhoto } from './chat-photo' import { MtCuteArgumentError } from '../errors' import { makeInspectable } from '../utils' import { assertTypeIs } from '../../utils/type-assertion' +import { InputMediaLike } from '../media' export namespace User { /** @@ -356,6 +357,32 @@ export class User { }, ]) } + + /** + * Send a text message to this user. + * + * @param text Text of the message + * @param params + */ + sendText( + text: string, + params?: Parameters[2] + ): ReturnType { + return this.client.sendText(this.inputPeer, text, params) + } + + /** + * Send a media to this user. + * + * @param media Media to send + * @param params + */ + sendMedia( + media: InputMediaLike, + params?: Parameters[2] + ): ReturnType { + return this.client.sendMedia(this.inputPeer, media, params) + } } makeInspectable(User) diff --git a/packages/dispatcher/src/filters.ts b/packages/dispatcher/src/filters.ts index 0d820c34..df043ee9 100644 --- a/packages/dispatcher/src/filters.ts +++ b/packages/dispatcher/src/filters.ts @@ -27,6 +27,9 @@ import { ChatMemberUpdate } from './updates' import { ChosenInlineResult } from './updates/chosen-inline-result' import { MessageAction } from '@mtcute/client/src/types/messages/message-action' import { UpdateState } from './state' +import { UserStatusUpdate } from './updates/user-status-update' +import { PollVoteUpdate } from './updates/poll-vote' +import { UserTypingUpdate } from './updates/user-typing-update' /** * Type describing a primitive filter, which is a function taking some `Base` @@ -332,19 +335,97 @@ export namespace filters { */ export const chat = ( type: T - ): UpdateFilter - sender: T extends 'private' | 'bot' | 'group' ? User : User | Chat - }> => (msg) => - msg.chat.type === type + ): UpdateFilter< + Message, + { + chat: Modify + sender: T extends 'private' | 'bot' | 'group' ? User : User | Chat + } + > => (msg) => msg.chat.type === type /** - * Filter messages by chat ID + * Filter updates by chat ID(s) */ - export const chatId = ( - id: number - ): UpdateFilter => (msg) => - msg.chat.id === id + export const chatId = (id: MaybeArray): UpdateFilter => { + if (Array.isArray(id)) { + const index: Record = {} + id.forEach((id) => (index[id] = true)) + + return (msg) => msg.chat.id in index + } + + return (msg) => msg.chat.id === id + } + + /** + * Filter updates by user ID(s) + * + * For chat member updates, uses `user.id` + */ + export const userId = ( + id: MaybeArray + ): UpdateFilter< + | Message + | InlineQuery + | ChatMemberUpdate + | ChosenInlineResult + | CallbackQuery + | PollVoteUpdate + | UserStatusUpdate + | UserTypingUpdate + > => { + if (Array.isArray(id)) { + const index: Record = {} + id.forEach((id) => (index[id] = true)) + + return (upd) => { + const ctor = upd.constructor + + if (ctor === Message) { + return (upd as Message).sender.id in index + } else { + if ( + ctor === UserStatusUpdate || + ctor === UserTypingUpdate + ) { + return ( + (upd as UserStatusUpdate | UserTypingUpdate) + .userId in index + ) + } else { + return ( + (upd as Exclude< + typeof upd, + Message | UserStatusUpdate | UserTypingUpdate + >).user.id in index + ) + } + } + } + } + + return (upd) => { + const ctor = upd.constructor + + if (ctor === Message) { + return (upd as Message).sender.id === id + } else { + if (ctor === UserStatusUpdate || ctor === UserTypingUpdate) { + return ( + (upd as UserStatusUpdate | UserTypingUpdate).userId === + id + ) + } else { + return ( + (upd as Exclude< + typeof upd, + Message | UserStatusUpdate | UserTypingUpdate + >).user.id === id + ) + } + } + } + } /** * Filter incoming messages. @@ -702,6 +783,36 @@ export namespace filters { return (upd) => upd.type === types } + /** + * Create a filter for {@link UserStatusUpdate} by new user status + * + * @param statuses Update type(s) + * @link User.Status + */ + export const userStatus: { + (status: T): UpdateFilter< + UserStatusUpdate, + { + type: T + lastOnline: T extends 'offline' ? Date : null + nextOffline: T extends 'online' ? Date : null + } + > + (statuses: T): UpdateFilter< + UserStatusUpdate, + { type: T[number] } + > + } = (statuses: MaybeArray): UpdateFilter => { + if (Array.isArray(statuses)) { + const index: Partial> = {} + statuses.forEach((typ) => (index[typ] = true)) + + return (upd) => upd.status in index + } + + return (upd) => upd.status === statuses + } + /** * Create a filter for {@link ChatMemberUpdate} for updates * regarding current user diff --git a/packages/dispatcher/src/propagation.ts b/packages/dispatcher/src/propagation.ts index 9658b177..a7622cce 100644 --- a/packages/dispatcher/src/propagation.ts +++ b/packages/dispatcher/src/propagation.ts @@ -27,7 +27,8 @@ export const ContinuePropagation: unique symbol = _sym.for( 'mtcute:ContinuePropagation' ) -export type PropagationSymbol = - | typeof StopPropagation - | typeof ContinuePropagation - | typeof StopChildrenPropagation +export type PropagationSymbol = symbol +// this seems to cause issues after publishing +// | typeof StopPropagation +// | typeof ContinuePropagation +// | typeof StopChildrenPropagation