Telegram Newsletter Command
Tags: php, laravel, cli
Published: 2020-12-13 18:22:11

After I've published my first post and got kind feedback from several readers I wanted to add something like a newsletter for my blog.

My goal is that it should inform the subscribers short after a post is published about the new post. Emails aren't an option as I hate emails and it's also not that easy and cheap to potentially scale them.
So I've searched for a different solution and thought about Telegram which has a powerful but still easy API and even creating a bot is as easy as sending messages to [BotFather](https://t.me/BotFather).

The next criteria are that I don't have any permanently running app for this site and my content is stored in markdown files. So it should work without any database and I need a runner that allows me to schedule commands.
The runner was quickly found, the same that deploys this site and does some checks - GitHub Actions.

## Setting up Telegram

That's probably the easiest part as you only send some messages and have to create a new Telegram channel.

### Bot

The commands I've sent to [BotFather](https://t.me/BotFather):

```
/newbot
/setdomain
/setuserpic
/setabouttext
```

After I've followed all the instructions I had my own `GummibeerBot`.

### Channel

So now we need a channel that the bot will send the messages to. That's possible via the normal UI.
You must create a `public` channel as bots can't send messages to private channels. And you also want others to be able to join easily - otherwise it wouldn't be a newsletter. 😉

After you've created your channel you must add the bot as an administrator with the ability to send messages.
To do so open the channel => click on the name => click on administrators and add a new one.

## Console Command

I've used `artisan make:command PromotePost` to create my new command - you can adjust the name however you want it.

Our command has to do two things:

1. pick a post to promote - it has to be published, should be promotable and shouldn't be promoted earlier
2. generate the message and send it to the Telegram channel

### Configuration

At first we have to configure our app to know about some secrets that shouldn't be part of our app code.

I will use the `services` config as it's perfect for simple service configurations like Telegram in this case.
I will add the `bot_token` (you've received during setting up the Telegram bot and the Telegram channel `chat_id`.

```php
'telegram' => [
    'bot_token' => env('TELEGRAM_BOT_TOKEN'),
    'chat_id' => '-xxxxxxxxxx',
],
```

The token is a secret you shouldn't share with anyone - so using the `.env` here is a good choice.
The chat ID isn't secret so it's up to you if you want to be able to adjust it via `.env` or not. Using env here would be useful if you want to use a test channel during development for example.

To retrieve the chat ID I've used a trick as the ID isn't copyable somewhere in the Telegram app.

You should send a message to your bot in the channel you want him to send the messages in - I've used `@GummibeerBot test`.
After this you should run the following code - [Tinkerwell](https://tinkerwell.app/) will be perfect for this but you can also use normal tinker or however you run one-shot code.

```php
$chatId = Http::get(sprintf(
    'https://api.telegram.org/bot%s/getUpdates',
    config('services.telegram.bot_token')
))->json()['result'][0]['channel_post']['chat']['id'];
```

This should return the chat ID which looks something like `-xxxxxxxxxx`.

### Picking your post

This part depends massively on your way you manage your posts and which attributes you use.
I've added a `should_promote` boolean attribute that indicates if this post should be promoted and a `promoted_at` datetime attribute that will indicate when/if the post was already promoted.
With these attributes you can use database query or collection filters to reduce your dataset.
As I only want to promote one post at a time I'm using this snippet to get the post I want to send out.

```php
$post = $posts->sortBy('created_at')->first();
```

### Send message via Telegram

That's the most important part but it's still not complicated thanks to Laravel `Http` helper class.

To send a message via Telegram you have to use the [sendMessage](https://core.telegram.org/method/messages.sendMessage) endpoint.

```php 
use Illuminate\Support\Facades\Http;
use Spatie\Emoji\Emoji;

$response = Http::post(
    sprintf('https://api.telegram.org/bot%s/sendMessage', config('services.telegram.bot_token')),
    [
        'chat_id' => config('services.telegram.chat_id'),
        'text' => Emoji::orangeBook().' '.$post->title.PHP_EOL.$post->url,
    ]
);
```

I'm using the [spatie/emoji](https://github.com/spatie/emoji) package here to get the "📙" emoji and append the post title and after a line break the URL to the post (be sure that your code generates an absolute URL to your production system).

That's it - you've already sent the message to your Telegram channel and should see a message in it.

After this you should update/save your promoted post to prevent it from being sent out multiple times.

For everyone interested to see it in action you can join my [Telegram Newsletter](https://t.me/GummibeerDev) channel.