This commit is contained in:
teidesu 2021-06-12 01:13:02 +03:00
parent 46317e8ddb
commit 707e317e16
5 changed files with 222 additions and 14 deletions

View file

@ -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[]

View file

@ -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<void> {
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<TelegramClient['sendText']>[2]
): ReturnType<TelegramClient['sendText']> {
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<TelegramClient['sendText']>[2]
): ReturnType<TelegramClient['sendText']> {
return this.client.sendMedia(this.inputPeer, media, params)
}
}
makeInspectable(Chat, [], ['user'])

View file

@ -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<TelegramClient['sendText']>[2]
): ReturnType<TelegramClient['sendText']> {
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<TelegramClient['sendText']>[2]
): ReturnType<TelegramClient['sendText']> {
return this.client.sendMedia(this.inputPeer, media, params)
}
}
makeInspectable(User)

View file

@ -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 = <T extends Chat.Type>(
type: T
): UpdateFilter<Message, {
chat: Modify<Chat, { type: T }>
sender: T extends 'private' | 'bot' | 'group' ? User : User | Chat
}> => (msg) =>
msg.chat.type === type
): UpdateFilter<
Message,
{
chat: Modify<Chat, { type: T }>
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<Message> => (msg) =>
msg.chat.id === id
export const chatId = (id: MaybeArray<number>): UpdateFilter<Message> => {
if (Array.isArray(id)) {
const index: Record<number, true> = {}
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<number>
): UpdateFilter<
| Message
| InlineQuery
| ChatMemberUpdate
| ChosenInlineResult
| CallbackQuery
| PollVoteUpdate
| UserStatusUpdate
| UserTypingUpdate
> => {
if (Array.isArray(id)) {
const index: Record<number, true> = {}
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: {
<T extends User.Status>(status: T): UpdateFilter<
UserStatusUpdate,
{
type: T
lastOnline: T extends 'offline' ? Date : null
nextOffline: T extends 'online' ? Date : null
}
>
<T extends User.Status[]>(statuses: T): UpdateFilter<
UserStatusUpdate,
{ type: T[number] }
>
} = (statuses: MaybeArray<User.Status>): UpdateFilter<UserStatusUpdate> => {
if (Array.isArray(statuses)) {
const index: Partial<Record<User.Status, true>> = {}
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

View file

@ -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