Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Major] Make fmt use intuitive #17

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"deno.enable": true,
"deno.lint": true
}
57 changes: 18 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,32 @@
# Parse Mode plugin for grammY

This plugin provides a transformer for setting default `parse_mode`, and a middleware for hydrating `Context` with familiar `reply` variant methods - i.e. `replyWithHTML`, `replyWithMarkdown`, etc.
This plugin lets you format your messages and captions with **bold**, __italic__, etc in a much more reliable and efficient way than with Markdown or HTML.
It does this by providing a transformer that injects `entities` or `caption_entities` if `text` or `caption` are FormattedString.

## Usage (Using format)

```ts
import { Bot, Composer, Context } from 'grammy';
import { bold, fmt, hydrateReply, italic } from '@grammyjs/parse-mode';
import type { ParseModeFlavor } from '@grammyjs/parse-mode';
import { Api, Bot, Context } from "grammy";
import { bold, fmt, hydrateFmt, underline } from "@grammyjs/parse-mode";
import type { ParseModeApiFlavor, ParseModeFlavor } from "@grammyjs/parse-mode";

const bot = new Bot<ParseModeFlavor<Context>>('');
type MyApi = ParseModeApiFlavor<Api>;
type MyContext = ParseModeFlavor<Context & { api: MyApi }>;
const bot = new Bot<MyContext, MyApi>("");

// Install format reply variant to ctx
bot.use(hydrateReply);
// Install automatic entities inject from FormattedString transformer
bot.api.config.use(hydrateFmt());

bot.command('demo', async ctx => {
await ctx.replyFmt(fmt`${bold('bold!')}
${bold(italic('bitalic!'))}
${bold(fmt`bold ${link('blink', 'example.com')} bold`)}`);
bot.command("demo", async (ctx) => {
const boldText = fmt`This is a ${bold("bolded")} string`;
await ctx.reply(boldText);

// fmt can also be called like a regular function
await ctx.replyFmt(fmt(['', ' and ', ' and ', ''], fmt`${bold('bold')}`, fmt`${bold(italic('bitalic'))}`, fmt`${italic('italic')}`));
});

bot.start();
```

## Usage (Using default parse mode and utility reply methods)

```ts
import { Bot, Composer, Context } from 'grammy';
import { hydrateReply, parseMode } from '@grammyjs/parse-mode';

import type { ParseModeFlavor } from '@grammyjs/parse-mode';

const bot = new Bot<ParseModeFlavor<Context>>('');

// Install familiar reply variants to ctx
bot.use(hydrateReply);

// Sets default parse_mode for ctx.reply
bot.api.config.use(parseMode('MarkdownV2'));
const underlineText = fmt`This is an ${underline("underlined")}`;
await ctx.api.sendMessage(ctx.chat.id, underlineText);

bot.command('demo', async ctx => {
await ctx.reply('*This* is _the_ default `formatting`');
await ctx.replyWithHTML('<b>This</b> is <i>withHTML</i> <code>formatting</code>');
await ctx.replyWithMarkdown('*This* is _withMarkdown_ `formatting`');
await ctx.replyWithMarkdownV1('*This* is _withMarkdownV1_ `formatting`');
await ctx.replyWithMarkdownV2('*This* is _withMarkdownV2_ `formatting`');
// fmt can also be use to concat FormattedStrings
const combinedText = fmt`${boldText}\n${underlineText}`;
await bot.api.sendMessage(ctx.chat.id, combinedText);
});

bot.start();
Expand Down
7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@
"build": "deno2node tsconfig.json"
},
"devDependencies": {
"@grammyjs/types": "^3.0.3",
"@tsconfig/node16": "^1.0.2",
"@types/node": "^16.6.1",
"deno2node": "^1.8.1",
"grammy": "^1.15.3"
"@types/node": "^20.4.7",
"deno2node": "^1.9.0",
"grammy": "^1.17.2"
},
"files": [
"dist/"
Expand Down
57 changes: 18 additions & 39 deletions src/README.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,32 @@
# Parse Mode plugin for grammY

This plugin provides a transformer for setting default `parse_mode`, and a middleware for hydrating `Context` with familiar `reply` variant methods - i.e. `replyWithHTML`, `replyWithMarkdown`, etc.
This plugin provides transformer that injects `entities` or `caption_entities`
if `text` or `caption` are FormattedString.

## Usage (Using format)

```ts
import { Bot, Composer, Context } from 'grammy';
import { bold, fmt, hydrateReply, italic } from '@grammyjs/parse-mode';
import type { ParseModeFlavor } from '@grammyjs/parse-mode';
import { Api, Bot, Context } from "grammy";
import { bold, fmt, hydrateFmt, underline } from "@grammyjs/parse-mode";
import type { ParseModeApiFlavor, ParseModeFlavor } from "@grammyjs/parse-mode";

const bot = new Bot<ParseModeFlavor<Context>>('');
type MyApi = ParseModeApiFlavor<Api>;
type MyContext = ParseModeFlavor<Context & { api: MyApi }>;
const bot = new Bot<MyContext, MyApi>("");

// Install format reply variant to ctx
bot.use(hydrateReply);
// Install automatic entities inject from FormattedString transformer
bot.api.config.use(hydrateFmt());

bot.command('demo', async ctx => {
await ctx.replyFmt(fmt`${bold('bold!')}
${bold(italic('bitalic!'))}
${bold(fmt`bold ${link('blink', 'example.com')} bold`)}`);
bot.command("demo", async (ctx) => {
const boldText = fmt`This is a ${bold("bolded")} string`;
await ctx.reply(boldText);

// fmt can also be called like a regular function
await ctx.replyFmt(fmt(['', ' and ', ' and ', ''], fmt`${bold('bold')}`, fmt`${bold(italic('bitalic'))}`, fmt`${italic('italic')}`));
});

bot.start();
```

## Usage (Using default parse mode and utility reply methods)

```ts
import { Bot, Composer, Context } from 'grammy';
import { hydrateReply, parseMode } from '@grammyjs/parse-mode';

import type { ParseModeFlavor } from '@grammyjs/parse-mode';

const bot = new Bot<ParseModeFlavor<Context>>('');

// Install familiar reply variants to ctx
bot.use(hydrateReply);

// Sets default parse_mode for ctx.reply
bot.api.config.use(parseMode('MarkdownV2'));
const underlineText = fmt`This is an ${underline("underlined")}`;
await ctx.api.sendMessage(ctx.chat.id, underlineText);

bot.command('demo', async ctx => {
await ctx.reply('*This* is _the_ default `formatting`');
await ctx.replyWithHTML('<b>This</b> is <i>withHTML</i> <code>formatting</code>');
await ctx.replyWithMarkdown('*This* is _withMarkdown_ `formatting`');
await ctx.replyWithMarkdownV1('*This* is _withMarkdownV1_ `formatting`');
await ctx.replyWithMarkdownV2('*This* is _withMarkdownV2_ `formatting`');
// fmt can also be use to concat FormattedStrings
const combinedText = fmt`${boldText}\n${underlineText}`;
await bot.api.sendMessage(ctx.chat.id, combinedText);
});

bot.start();
Expand Down
5 changes: 3 additions & 2 deletions src/deps.deno.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
export type {
Api,
Context,
NextFunction,
Transformer,
} from "https://lib.deno.dev/x/grammy@^1.0/mod.ts";
export type {
InputTextMessageContent,
MessageEntity,
ParseMode,
} from "https://esm.sh/@grammyjs/types@2";
} from "https://lib.deno.dev/x/grammy@^1.0/types.ts";
4 changes: 2 additions & 2 deletions src/deps.node.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export type { Context, NextFunction, Transformer } from "grammy";
export type { MessageEntity, ParseMode } from "@grammyjs/types";
export type { Api, Context, NextFunction, Transformer } from "grammy";
export type { InputTextMessageContent, MessageEntity } from "grammy/types";
35 changes: 23 additions & 12 deletions src/format.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { MessageEntity } from "./deps.deno.ts";

/**
* Objects that implement this interface implement a `.toString()`
* Objects that implement this interface implement a `.toString()`
* method that returns a `string` value representing the object.
*/
export interface Stringable {
Expand All @@ -26,11 +26,11 @@ class FormattedString implements Stringable {
entities: MessageEntity[];

/**
* Creates a new `FormattedString`. Useful for constructing a
* Creates a new `FormattedString`. Useful for constructing a
* `FormattedString` from user's formatted message
* @param text Plain text value
* @param entities Format entities
*
*
* ```ts
* // Constructing a new `FormattedString` from user's message
* const userMsg = new FormattedString(ctx.message.text, ctx.entities());
Expand Down Expand Up @@ -147,34 +147,45 @@ const customEmoji = (placeholder: Stringable, emoji: number) => {
* @param chatId The chat ID to link to.
* @param messageId The message ID to link to.
*/
const linkMessage = (stringLike: Stringable, chatId: number, messageId: number) => {
const linkMessage = (
stringLike: Stringable,
chatId: number,
messageId: number,
) => {
if (chatId > 0) {
console.warn("linkMessage can only be used for supergroups and channel messages. Refusing to transform into link.");
console.warn(
"linkMessage can only be used for supergroups and channel messages. Refusing to transform into link.",
);
return stringLike;
} else if (chatId < -1002147483647 || chatId > -1000000000000) {
console.warn("linkMessage is not able to link messages whose chatIds are greater than -1000000000000 or less than -1002147483647 at this moment. Refusing to transform into link.");
console.warn(
"linkMessage is not able to link messages whose chatIds are greater than -1000000000000 or less than -1002147483647 at this moment. Refusing to transform into link.",
);
return stringLike;
} else {
return link(stringLike, `https://t.me/c/${(chatId + 1000000000000) * -1}/${messageId}`);
return link(
stringLike,
`https://t.me/c/${(chatId + 1000000000000) * -1}/${messageId}`,
);
}
};

// === Format tagged template function

/**
* This is the format tagged template function. It accepts a template literal
* containing any mix of `Stringable` and `string` values, and constructs a
* This is the format tagged template function. It accepts a template literal
* containing any mix of `Stringable` and `string` values, and constructs a
* `FormattedString` that represents the combination of all the given values.
* The constructed `FormattedString` also implements Stringable, and can be used
* The constructed `FormattedString` also implements Stringable, and can be used
* in further `fmt` tagged templates.
* @param rawStringParts An array of `string` parts found in the tagged template
* @param stringLikes An array of `Stringable`s found in the tagged template
*
*
* ```ts
* // Using return values of fmt in fmt
* const left = fmt`${bold('>>>')} >>>`;
* const right = fmt`<<< ${bold('<<<')}`;
*
*
* const final = fmt`${left} ${ctx.msg.text} ${right}`;
* await ctx.replyFmt(final);
* ```
Expand Down
Loading