DenoとGithub ActionsでZennの記事を自動定期宣伝ツイートする
最近、Denoにハマっています。
Denoで何かを自動化したいと思い、Zennで書いた記事をツイートすることを考えました。
DenoでZennの情報を取得する
先日、DenoでZennの情報を取得するモジュールを作りました。
これをつかってZennの自分の最新記事の情報を取得します。
import {
zennApi,
} from "https://github.com/kawarimidoll/deno-zenn-api/raw/main/mod.ts";
const { articles } = await zennApi("kawarimidoll");
const article = articles[0];
if (!article) {
throw new Error("No articles found");
}
console.log({ article });
毎回deno run --allow-net
のようにパーミッションを書くのが億劫なので、Velociraptorを使います。
設定ファイルを作ります。
allow:
- net
scripts:
promote_zenn_article:
cmd: promote_zenn_article.ts
これで実行してみます。
❯ vr promote_zenn_article
{
article: {
id: 41325,
title: "DenoでZennのAPIモジュールを作る",
slug: "3d51924dc595b6",
published: true,
commentsCount: 0,
likedCount: 3,
bodyLettersCount: 5113,
readingTime: 5,
articleType: "tech",
emoji: "🦕",
isSuspendingPrivate: false,
publishedAt: "2021-06-26T12:19:10.637+09:00",
bodyUpdatedAt: null,
sourceRepoUpdatedAt: null,
createdAt: "2021-06-25T20:43:24.621+09:00",
updatedAt: "2021-06-27T06:45:15.675+09:00",
user: {
id: 39895,
username: "kawarimidoll",
name: "kawarimidoll",
avatarSmallUrl: "https://storage.googleapis.com/zenn-user-upload/avatar/icon_2379ac8d86.jpeg"
},
topics: [
{
id: 57,
name: "html",
displayName: "HTML",
taggingsCount: 290,
imageUrl: "https://storage.googleapis.com/zenn-user-upload/topics/171432f5a6.png"
},
{
id: 218,
name: "api",
displayName: "API",
taggingsCount: 164,
imageUrl: "https://storage.googleapis.com/zenn-user-upload/topics/204f07b4d5.png"
},
{ id: 1843, name: "dom", displayName: "dom", taggingsCount: 17, imageUrl: null },
{
id: 181,
name: "zenn",
displayName: "Zenn",
taggingsCount: 276,
imageUrl: "https://storage.googleapis.com/zenn-user-upload/topics/0b0064a451.jpeg"
},
{
id: 180,
name: "deno",
displayName: "Deno",
taggingsCount: 72,
imageUrl: "https://storage.googleapis.com/zenn-topics/deno.png"
}
]
}
}
こんな感じのデータが取得できます。
ここからtitle
やtopics
の内容を使ってツイート文面を作れそうです。
Zenn記事へのリンクを生成する
データには記事へのリンクがないことに気づきます。
いろいろなページを見てみると、リンクはhttps://zenn.dev/[username]/[articles|books|scraps]/[slug]
のような形式になっているようです。
これを生成する機能をdeno_zenn_api
リポジトリへ追加しました。
import { ZENN_ROOT } from "./zenn_api.ts";
import {
implementsZennArticle,
implementsZennBook,
ZennArticle,
ZennBook,
ZennScrap,
} from "./types.ts";
const zennLink = (
object: ZennArticle | ZennBook | ZennScrap,
): string => {
if (!object) {
return ZENN_ROOT;
}
const username = object.user.username;
const resourceName = implementsZennArticle(object)
? "/articles/"
: implementsZennBook(object)
? "/book/"
: "/scrap/";
return ZENN_ROOT + username + resourceName + object.slug;
};
export { zennLink };
渡されたリソースがarticle
かbook
かscrap
かを調べ、リンクになるように文字列を連結します。
これを読み込んで使います。
import {
zennApi,
+ zennLink,
} from "https://github.com/kawarimidoll/deno-zenn-api/raw/main/mod.ts";
const { articles } = await zennApi("kawarimidoll");
const article = articles[0];
if (!article) {
throw new Error("No articles found");
}
+ const message = `『${article.title}』という記事を書きました
+ ${zennLink(article)}`;
+ console.log(message);
実行してみます。
❯ vr promote_zenn_article
『DenoでZennのAPIモジュールを作る』という記事を書きました
https://zenn.dev/kawarimidoll/articles/3d51924dc595b6
リンク文字列の生成ができました。
ほかにもreadingTime
やtopics
の情報も入れていくことができます。
このへんはあまり技術に関係しないので割愛します。
ローカルからツイートする
続いて実際にツイートします。
以前作ったIFTTTのAPIを使います。
もちろん、Twitter公式のほうで開発者登録をしていればそのAPIを使えると思います。
ここでは、ツイート文面とAPIキーを受け取るasync sendTweet(params: { message: string; key: string })
が定義されているものとし、これを使っていきます。
といっても、import
して呼び出せばよいだけですね。
+ import { IFTTT_WEBHOOK_KEY } from "./env.ts";
+ import { sendTweet } from "./tweet_with_ifttt.ts";
(略)
console.log(message);
+ console.log(await sendTweet({ message, key: IFTTT_WEBHOOK_KEY }));
なお、環境変数を読み込むenv.ts
に関しては以前の記事で説明しています。
velociraptor.yml
で環境変数を読めるようにしておきます。
allow:
+ - read=.env,.env.example,.env.defaults
+ - env
- net
scripts:
promote_zenn_article:
cmd: promote_zenn_article.ts
実行します。
※実施したタイミングの関係で前述のものとは抽出している記事が異なりますが、ここでは実行結果の例示なので気にしないでください。
❯ vr promote_zenn_article
『DenoでKyを使って極楽要求(しなさい)』という #Zenn 記事を書きました
kyとかfetchとかDenoとかについていろいろ書いています
5分くらいで読めるのでスキマ時間のお供にどうぞ
https://zenn.dev/kawarimidoll/articles/13c3f75f6f22d6
(本ツイートはDeno🦕で自動生成しています)
Congratulations! You've fired the send_tweet event
なお、IFTTTを経由したことでリンクがift.tt
の短縮リンクになっていますが、別に問題はないと思います。
GitHub Actionsから実行する
これを自動で定期実行したいと思います。
方法はいろいろあると思いますが、今回はGithub Actionsを使いました。
環境変数の設定
ツイートAPIを使用するため、環境変数(今回はIFTTT_WEBHOOK_KEY
)を設定します。
GitHubのリポジトリのSettingsからSecretsへ入り、New repository secretします。
値を入れてAdd Secretします。
一覧に追加されればOKです。
Updateボタンから値の変更をすることはできますが、設定された値を表示する方法はありません。
環境変数の使用
設定した環境変数は、Github Actionsの設定ファイル内で、${{ secrets.ENV_NAME }}
の形式で使用できます。
ということで、GitHub Actions内で.env
ファイルを生成し、必要な環境変数を適用すればOKです。
- name: Create env file
run: |
touch .env
echo IFTTT_WEBHOOK_KEY=${{ secrets.IFTTT_WEBHOOK_KEY }} >> .env
しかし、dotenvのsafe modeを使用しており、同一リポジトリの別の箇所で他の環境変数を使っている場合には問題が起こります。
.env.example
にIFTTT_WEBHOOK_KEY
以外の環境変数が定義されているにも関わらず、.env
にIFTTT_WEBHOOK_KEY
しか無いため、エラーが発生します。
IFTTT_WEBHOOK_KEY=ifttt_webhook_key
OTHER_KEY=other_key
IFTTT_WEBHOOK_KEY=xxxxxxxxxxx
error: Uncaught MissingEnvVarsError: The following variables were defined in the example file but are not present in the environment:
OTHER_KEY
Make sure to add them to your env file.
したがって.env.example
をコピーして枠組みを作り、必要な環境変数だけ適用する方法を取ります。
- name: Create env file
run: |
- touch .env
+ cp .env.example .env
echo IFTTT_WEBHOOK_KEY=${{ secrets.IFTTT_WEBHOOK_KEY }} >> .env
これで生成される.env
は以下の形式になります。
.env.example
で定義されているすべての値が含まれているため、エラーは生じません。
また、キーが重複している場合は後のもので上書きされるため、正しいsecretが使われます。
IFTTT_WEBHOOK_KEY=ifttt_webhook_key
OTHER_KEY=other_key
IFTTT_WEBHOOK_KEY=xxxxxxxxxxx
yml全体像
velociraptorのアクションが提供されているので、それを使って実行します。
schedule
の設定、前述の環境変数の設定とあわせ、全体としてはこのようになります。
schedule
の設定の際は、UTCで解釈されることに注意してください。また、GitHubのサーバーの稼働状態により、実行に遅延が生じることがあるようです。
name: tweet_promote_zenn_article
on:
# 03:20UTC -> 12:20JST
schedule:
- cron: "20 3 * * *"
jobs:
promote:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: denoland/setup-deno@v1
- uses: jurassiscripts/setup-velociraptor@v1
- name: Create env file
run: |
cp .env.example .env
echo IFTTT_WEBHOOK_KEY=${{ secrets.IFTTT_WEBHOOK_KEY }} >> .env
- run: vr promote_zenn_article
これで毎日お昼ごろに最新記事がツイートされるようになりました。
おわりに
GitHub ActionsからDenoを自動実行することができました。
今回は「毎日」「最新記事を」「ツイート」してみましたが、
- 間隔を変える
- 毎週や毎月実行する
- workflow_dispatchでAPIにする
- インプットを変える
- ランダムで記事を紹介する
- LIKE数の多い記事を紹介する
- Zennの人気記事を(勝手に)紹介する
- アウトプットを変える
…などにも応用できると思います。夢が広がる。
こんな感じでDenoで遊んでいるリポジトリはこちらです。
参考
Discussion