Last active
March 28, 2023 10:39
-
-
Save wojpawlik/2b1b226d52d675ec246c6f8abdab81ef to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { Client, Opts, Ret } from "./deps.ts"; | |
export type Api = { | |
[M in keyof Opts]: ( | |
payload: Opts[M], | |
signal?: AbortSignal, | |
) => Promise<Ret[M]>; | |
}; | |
export const createApi = (client: Client) => | |
new Proxy({}, { | |
get: | |
<M extends keyof Opts>(_: unknown, method: M) => | |
async (payload: Opts[M], signal?: AbortSignal) => { | |
const result = await client.call({ method, payload, signal }); | |
if (!result.ok) throw result; | |
return result.result; | |
}, | |
}) as Api; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createApi } from "./api.ts"; | |
import type { Middleware } from "./compose.ts"; | |
import type { Context } from "./context.ts"; | |
import type { Client, Update } from "./deps.ts"; | |
export async function noop() {} | |
export async function createBot( | |
client: Client, | |
middleware: Middleware<Context>, | |
) { | |
const api = createApi(client); | |
const handler = middleware(noop); | |
const botInfo = await api.getMe({}); | |
const handleUpdate = (update: Update) => handler({ update, api, botInfo }); | |
return { api, botInfo, client, handleUpdate } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// deno-lint-ignore-file ban-types | |
import type { Update } from "https://esm.sh/typegram@^3.12"; | |
import type { Context } from "./context.ts"; | |
type Handler<C extends Context> = (ctx: C) => Promise<void>; | |
type EndoFunction<T> = (t: T) => T; | |
export type Extension<C extends Context, E extends object> = (ctx: C) => E; | |
export type Filter<U extends Update> = (update: Update) => update is U; | |
export type Middleware<C extends Context> = EndoFunction<Handler<C>>; | |
export function compose<T>(...fns: EndoFunction<T>[]): EndoFunction<T> { | |
return (next: T) => fns.reduceRight((t, fn) => fn(t), next); | |
} | |
export function on<C extends Context, U extends Update>( | |
filter: Filter<U>, | |
middleware: Middleware<C & { update: U }>, | |
): Middleware<C> { | |
return (next) => { | |
const handler = middleware(next); | |
return (ctx) => | |
filter(ctx.update) ? handler(ctx as C & { update: U }) : next(ctx); | |
}; | |
} | |
export function using<C extends Context, E extends object>( | |
extension: Extension<C, E>, | |
middleware: Middleware<C & E>, | |
): Middleware<C> { | |
return (next) => { | |
const handler = middleware(next); | |
return (ctx) => handler(Object.assign(Object.create(ctx), extension(ctx))); | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { Api } from "./api.ts"; | |
import type { Update, UserFromGetMe } from "./deps.ts"; | |
import type { Deunionize } from "./deunionize.ts"; | |
export interface Context { | |
readonly update: Deunionize<Update>; | |
readonly api: Api; | |
readonly botInfo: UserFromGetMe; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export * from "https://esm.sh/@telegraf/client@^0.6.1"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// deno-lint-ignore-file ban-types | |
export type UnionKeys<T> = T extends unknown ? keyof T : never; | |
type AddOptionalKeys<K extends PropertyKey> = { readonly [P in K]?: never }; | |
/** @see https://millsp.github.io/ts-toolbelt/modules/union_strict.html */ | |
export type Deunionize< | |
B extends object | undefined, | |
T extends B = B, | |
> = T extends object ? T & AddOptionalKeys<Exclude<UnionKeys<B>, keyof T>> : T; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// deno-lint-ignore-file ban-types | |
import type { | |
CommonMessageBundle, | |
Message, | |
Update, | |
} from "https://esm.sh/typegram@^3.12"; | |
import type { Deunionize, UnionKeys } from "./deunionize.ts"; | |
type DistinctKeys<T extends object> = Exclude<UnionKeys<T>, keyof T>; | |
type Keyed<T extends object, K extends DistinctKeys<T>> = | |
& Record<K, {}> | |
& Deunionize<Record<K, {}>, T>; | |
export const message = | |
<K extends DistinctKeys<Message>, Ks extends K[]>(...keys: Ks) => | |
( | |
update: Update, | |
): update is Update.MessageUpdate<Keyed<Message, Ks[number]>> => { | |
if (!("message" in update)) return false; | |
for (const key of keys) { | |
if (!(key in update.message)) return false; | |
} | |
return true; | |
}; | |
export const editedMessage = | |
<K extends DistinctKeys<CommonMessageBundle>, Ks extends K[]>(...keys: Ks) => | |
( | |
update: Update, | |
): update is Update.EditedMessageUpdate< | |
Keyed<CommonMessageBundle, Ks[number]> | |
> => { | |
if (!("edited_message" in update)) return false; | |
for (const key of keys) { | |
if (!(key in update.edited_message)) return false; | |
} | |
return true; | |
}; | |
export const channelPost = | |
<K extends DistinctKeys<Message>, Ks extends K[]>(...keys: Ks) => | |
( | |
update: Update, | |
): update is Update.ChannelPostUpdate<Keyed<Message, Ks[number]>> => { | |
if (!("channel_post" in update)) return false; | |
for (const key of keys) { | |
if (!(key in update.channel_post)) return false; | |
} | |
return true; | |
}; | |
export const editedChannelPost = | |
<K extends DistinctKeys<CommonMessageBundle>, Ks extends K[]>(...keys: Ks) => | |
( | |
update: Update, | |
): update is Update.EditedChannelPostUpdate< | |
Keyed<CommonMessageBundle, Ks[number]> | |
> => { | |
if (!("edited_channel_post" in update)) return false; | |
for (const key of keys) { | |
if (!(key in update.edited_channel_post)) return false; | |
} | |
return true; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment