Skip to content

Instantly share code, notes, and snippets.

@watzon
Created November 23, 2021 23:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save watzon/60b475ed5c0f427f29775bc9d4164cb2 to your computer and use it in GitHub Desktop.
Save watzon/60b475ed5c0f427f29775bc9d4164cb2 to your computer and use it in GitHub Desktop.
import { Api, TelegramClient } from 'telegram'
import { EditMessageParams, SendMessageParams } from 'telegram/client/messages';
import { Entity, EntityLike } from 'telegram/define'
import { EventBuilder, EventCommon } from "telegram/events/common";
import { toSignedLittleBuffer } from 'telegram/Helpers';
import { getInputPeer, _getEntityPair } from 'telegram/Utils';
import { escapeRegExp } from '../utils';
export interface NewCallbackQueryInterface {
chats: EntityLike[];
func?: { (event: NewCallbackQueryEvent): boolean };
fromUsers: EntityLike[];
blacklistUsers: EntityLike[];
pattern?: RegExp | string | ((data: string) => void);
}
export const NewCallbackQueryDefaults: NewCallbackQueryInterface = {
chats: [],
fromUsers: [],
blacklistUsers: [],
}
/**
* Occurs whenever you sign in as a bot and a user
* clicks one of the inline buttons on your messages.
* Note that the `chats` parameter will **not** work with normal
* IDs or peers if the clicked inline button comes from a "via bot"
* message. The `chats` parameter also supports checking against the
* `chat_instance` which should be used for inline callbacks.
*
* @example
* ```ts
* async function printQuery(event: NewCallbackQueryEvent) {
* // TODO
* }
* ```
*/
export class CallbackQueryEvent extends EventBuilder {
match?: RegExp
private _noCheck: boolean
constructor(inlineQueryParams: Partial<NewCallbackQueryInterface> = {}) {
const { chats, func, pattern } = inlineQueryParams;
super({ chats, func, blacklistChats: false })
if (typeof pattern === 'string') {
this.match = new RegExp(`^${escapeRegExp(pattern)}$`)
} else if (pattern instanceof RegExp) {
this.match = pattern
}
this._noCheck = [
this.chats,
this.func,
this.match
].every((i) => i === null || i === undefined)
}
build(update: Api.TypeUpdate | Api.TypeUpdates, others: any = null) {
if (update instanceof Api.UpdateBotCallbackQuery) {
return new NewCallbackQueryEvent(update, update.peer, update.msgId);
} else if (update instanceof Api.UpdateInlineBotCallbackQuery) {
const b = toSignedLittleBuffer(update.msgId.id, 8)
const msgId = b.readInt32LE()
const peerId = b.readInt32LE(4)
const peer = peerId < 0
? new Api.PeerChannel({ channelId: -peerId })
: new Api.PeerUser({ userId: peerId })
return new NewCallbackQueryEvent(update, peer, msgId)
}
}
filter(event: NewCallbackQueryEvent) {
if (this._noCheck) return event;
if (this.chats) {
let inside = this.chats.includes(event.query.chatInstance as unknown as EntityLike)
if (event.chatId) {
inside = inside || this.chats.includes(event.chatId)
}
if (inside === this.blacklistChats) {
return
}
}
if (this.match) {
const data = new TextDecoder().decode(event.query.data)
const result = this.match.exec(data)
if (result) {
event.patternMatch = result
} else {
return
}
}
if (this.func) {
return this.func(event)
}
return true
}
}
export interface AnswerCallbackQueryParams {
message: string;
cacheTime: number;
url: string;
alert: boolean;
}
export class NewCallbackQueryEvent extends EventCommon {
/**
* The original {@link Api.UpdateBotCallbackQuery} or {@link Api.UpdateInlineBotCallbackQuery} object.
*/
query: Api.UpdateBotCallbackQuery | Api.UpdateInlineBotCallbackQuery;
/**
* The regex match object returned from successfully matching the
* query `data` with the provided pattern in your event handler.
*/
patternMatch: RegExpMatchArray | undefined;
private _message: Api.Message | undefined;
private _answered: boolean;
private _senderId: number | undefined;
private _sender: Entity | undefined;
private _inputSender: Api.TypeInputPeer | undefined;
constructor(query: Api.UpdateBotCallbackQuery | Api.UpdateInlineBotCallbackQuery, peer: Api.TypePeer, msgId: number) {
super({
msgId,
chatPeer: peer,
broadcast: false,
});
this.query = query;
this.patternMatch = undefined;
this._senderId = query.userId;
this._message = undefined;
this._answered = false;
}
_setClient(client: TelegramClient) {
super._setClient(client);
const [sender, inputSender] = _getEntityPair(this._senderId, this._entities, client._entityCache);
this._sender = sender;
this._inputSender = inputSender;
}
get id() {
return this.query.queryId;
}
get messageId() {
return this._messageId;
}
get data() {
return this.query.data;
}
get chatInstance() {
return this.query.chatInstance;
}
async getMessage() {
if (this._message) {
return this._message;
}
const chat = this.isChannel ? await this.getInputChat() : undefined;
if (!chat) return;
const messages = await this._client.getMessages(chat, { ids: this._messageId })
this._message = messages[0];
return this._message;
}
async _refetchSender() {
if (this._entities.has(this._senderId)) {
this._sender = this._entities.get(this._senderId);
}
if (!this._sender) return;
this._inputSender = getInputPeer(this._chat)
if (!(this._inputSender.hasOwnProperty('accessHash'))) {
try {
this._inputSender = this._client._entityCache.get(this._senderId);
} catch (e) {
const m = await this.getMessage();
if (m) {
this._sender = m._sender;
this._inputSender = m._inputSender;
}
}
}
}
async answer({ message, cacheTime, url, alert }: Partial<AnswerCallbackQueryParams> = {}) {
if (this._answered) return;
return await this._client.invoke(new Api.messages.SetBotCallbackAnswer({
queryId: this.query.queryId,
cacheTime,
alert,
message,
url,
})).then((res) => {
this._answered = true;
return res;
})
}
get viaInline() {
return this.query instanceof Api.UpdateInlineBotCallbackQuery
}
async respond(params: SendMessageParams = {}) {
await this.answer()
await this._client.sendMessage(await this.getInputChat(), params)
}
async reply(params: SendMessageParams = {}) {
await this.answer()
params.replyTo = this.messageId
await this._client.sendMessage(await this.getInputChat(), params)
}
async edit(params: EditMessageParams) {
if ((this.query.msgId as any) instanceof Api.InputBotInlineMessageID) {
return await this._client.editMessage(this.messageId, params).then(async (res) => {
await this.answer();
return res;
})
} else {
return await this._client.editMessage(await this.getInputChat(), params).then(async (res) => {
await this.answer();
return res;
})
}
}
async delete({ revoke } = { revoke: false }) {
if (this._client) {
return this._client.deleteMessages(
await this.getInputChat(),
[this.messageId as any],
{ revoke })
}
}
get sender() {
return this._sender;
}
async getSender() {
if (
this._client &&
(!this._sender ||
(this._sender instanceof Api.Channel && this._sender.min)) &&
(await this.getInputSender())
) {
try {
this._sender = await this._client.getEntity(this._inputSender!);
} catch (e) {
await this._refetchSender();
}
}
return this._sender;
}
get inputSender() {
if (!this._inputSender && this._senderId && this._client) {
try {
this._inputSender = this._client._entityCache.get(
this._senderId
);
} catch (e) {}
}
return this._inputSender;
}
async getInputSender() {
if (!this.inputSender && this._senderId && this._client) {
await this._refetchSender();
}
return this._inputSender;
}
get senderId() {
return this._senderId;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment