1
0

qiita-cliからの投稿時にXに自動でポストできるようにする

Posted at

1. はじめに

qiita-cliというQiitaの記事をGithub上で管理投稿できるライブラリが公式から用意されています。

ブラウザから投稿すると投稿時にポストするかのモーダルが出てくれますが、qiita-cliからは対応してないみたいなので自動でポストするように改変してみます。
バージョンの追従が非常にめんどくさくなるのでオススメはしません。

ここからは企業様のレポジトリをforkして改変していくので一応ライセンス表記

qiita-cliは、Apache License 2.0の下でライセンスされています。詳細については、LICENSEファイルを参照してください。

2. 環境

・すでにqiita-cliのセットアップが完了しているローカルレポジトリ
・Node.js 18.18.0 以上 (↑がセットアップできているなら入っているはず)

3. Twitter API

事前にこちらの記事の手順で各種認証情報を取得しておきます。

必要な情報はこちらの画面の「API Key and Secret」と「Access Token and Secret」です。

スクリーンショット 2024-08-31 21.18.03.png

4. qiita-cli

forkしてくる

公式のレポジトリからfrokしました。

適当にdevというブランチを切りここで作業していきます。

Xにポストする処理

どこに作ってもいいんですがsrc/lib/以下にXにポストするための処理を用意しておきます。
先ほど用意したTwitterAPIの認証情報をここで環境変数から入れます。

tweet.ts
import { TwitterApi } from "twitter-api-v2";

export const tweet = async (args: string[]) => {
  // 環境変数が設定されているか確認
  const apiKey = process.env.TWITTER_API_KEY;
  const apiSecret = process.env.TWITTER_API_SECRET;
  const accessToken = process.env.TWITTER_ACCESS_TOKEN;
  const accessSecret = process.env.TWITTER_ACCESS_SECRET;

  if (!apiKey || !apiSecret || !accessToken || !accessSecret) {
    console.log("Twitter API keys are not set. Skipping tweet.");
    return;
  }

  // 環境変数が揃っている場合のみ、Twitter APIクライアントを初期化
  const client = new TwitterApi({
    appKey: apiKey,
    appSecret: apiSecret,
    accessToken: accessToken,
    accessSecret: accessSecret,
  });

  const message = args.join(" ");

  try {
    const tweetResponse = await client.v2.tweet(message);
    console.log("Successfully tweeted:", tweetResponse.data);
  } catch (err) {
    console.error("Failed to send tweet:", err);
  }
};

qiita-api

内部的にはqiita-apiを呼び出しているみたいでこちらをラップしているディレクトリが./qiita-api以下にあります。

こちらのPOST /api/v2/itemsを使って投稿しているみたいです。

このAPIで投稿時のURLがレスポンスに入っているみたいなんですが、qiita-cliでは受け取っているオブジェクトにフィールドが含まれていないので追加します。
この際、null許容で追加しないと予め用意されているテストでエラーが出たので?をつけています。

./src/qiita-api/index.ts
export interface Item {
  body: string;
  id: string;
  private: boolean;
  tags: {
    name: string;
  }[];
  title: string;
  organization_url_name: string | null;
  coediting: boolean;
  created_at: string;
  updated_at: string;
  slide: boolean;
  url?: string; // urlをnull許容で追加
}

投稿する時にポストする

src/commands/publish.tsが投稿するときのロジックっぽいのでこちらに先ほどのポストする処理を追加します。
長いので既存の処理は省略します。

./src/commands/publish.ts
// ---- 省略
import { tweet } from "../lib/tweet"; // インポート

// ---- 省略
  const promises = targetItems.map(async (item) => {
    let responseItem: Item;
    if (item.id) {
      responseItem = await qiitaApi.patchItem({
        rawBody: item.rawBody,
        tags: item.tags,
        title: item.title,
        uuid: item.id,
        isPrivate: item.secret,
        organizationUrlName: item.organizationUrlName,
        slide: item.slide,
      });

      console.log(`Updated: ${item.name} -> ${item.id}`);
    } else {
      responseItem = await qiitaApi.postItem({
        rawBody: item.rawBody,
        tags: item.tags,
        title: item.title,
        isPrivate: item.secret,
        organizationUrlName: item.organizationUrlName,
        slide: item.slide,
      });
      await fileSystemRepo.updateItemUuid(item.name, responseItem.id);

      // ---- ここを追加 Xにポストする処理を追加
      const tweetMessage = `記事を投稿しました!\n\n${responseItem.title}\n${responseItem.url}\n#Qiita`;
      try {
        console.log(`private is : ${responseItem.private}`);
        if(!responseItem.private){
          await tweet([tweetMessage]);
        }
      } catch (err) {
        console.error("Failed to post on Twitter:", err);
      }
      console.log(`Posted: ${item.name} -> ${responseItem.id}`);
      // ---- 追加ここまで
    }

    await fileSystemRepo.saveItem(responseItem, false, true);
  });

  // ---- 省略
};

限定記事の場合、ツイートされないようにしています。(限定記事書かないけど)

if(!responseItem.private){
  await tweet([tweetMessage]);
}

Github Actionの設定をしないならこれでqiita-cliの改変は終わりです。

Github Action

git push時に投稿とポストをしたい場合はこちらも改変していきます。
npm install -gしている部分を今作成している自分のレポジトリにしてローカルインストールにおきます。
また、qiitaコマンドの実行をnpxで行うようにしておきます。

./actions/publish/action.yml
name: "Publish articles to Qiita"
description: "Publish articles to Qiita using qiita-cli"
author: "Qiita Inc."

inputs:
  root:
    required: false
    default: "."
    description: "Root directory path"
  qiita-token:
    required: true
    description: "Qiita API token"

runs:
  using: "composite"
  steps:
    - uses: actions/setup-node@v4
      with:
        node-version: "20.16.0"
    - name: Install qiita-cli
      # ここを自分のレポジトリにする
      run: npm install github:Pokeyama/qiita-cli#dev
      shell: bash
    - name: Publish articles
      # npxで実行するようにしておく
      run: npx qiita publish --all --root ${{ inputs.root }}
      env:
        QIITA_TOKEN: ${{ inputs.qiita-token }}
      shell: bash
    - name: Commit and push diff # Not executed recursively even if `push` is triggered. See https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow
      run: |
        git add ${{ inputs.root }}/public/*
        if ! git diff --staged --exit-code --quiet; then
          git config --global user.name 'github-actions[bot]'
          git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com'
          git commit -m 'Updated by qiita-cli'
          git push
        fi
      shell: bash

ここがうまくいかなかった。
なんでかグローバルインストールだとqiitaコマンドが使えなくて苦戦。
色々あってこの形になりました。
ありがとうM平さん。

5. qiita-cliをインストールしているレポジトリ

ここからはqiita-cliを使用している記事管理レポジトリを少し改変していきます。

元のqiita-cliをアンインストールして、先ほど改変したレポジトリからnpm installします。

npm uninstall @qiita/qiita-cli   
npm install github:Pokeyama/qiita-cli#dev --save-dev 

.envに認証情報を書く

ルートディレクトリに.envファイルを作成して以下の対応する変数に最初用意したTwitterAPIの認証情報を書いておきます。
.gitignoreに追加するの忘れずに。

TWITTER_API_KEY=xxxxxxxxxxx
TWITTER_API_SECRET=xxxxxxxxxxx
TWITTER_ACCESS_TOKEN=xxxxxxxxxxx
TWITTER_ACCESS_SECRET=xxxxxxxxxxx

Github Actionを使わないならこの時点で自動ポストができているはずです。

npx qiita publish {記事名}

Github Action

git push時にも投稿したい場合こちらも追記します。

Github上のSettings > Secrets and variacles > Actions.envに入れた時と同じTwitterAPIの認証情報を入れます。

スクリーンショット 2024-08-31 22.15.20.png

ymlもTwitterAPIの認証情報を受け取るように編集します。

.github/workflows/publish.yml
# Please set 'QIITA_TOKEN' secret to your repository
name: Publish articles

on:
  push:
    branches:
      - main
      - master
  workflow_dispatch:

permissions:
  contents: write

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: false

jobs:
  publish_articles:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: Pokeyama/qiita-cli/actions/publish@dev
        with:
          qiita-token: ${{ secrets.QIITA_TOKEN }}
          root: "."
        # TwitterAPI認証情報を受け取れるようにしておく  
        env:
          TWITTER_API_KEY: ${{ secrets.TWITTER_API_KEY }}
          TWITTER_API_SECRET: ${{ secrets.TWITTER_API_SECRET }}
          TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
          TWITTER_ACCESS_SECRET: ${{ secrets.TWITTER_ACCESS_SECRET }}

以上でgit pull時にも投稿されるようになります。

まとめ

NodeもTypeScriptも素人なんでおかしいところがあるかもしれないです。
特にqiita-cli側のnpm install時にうまくコマンドをうまく利用できなかった理由が今でもわかってないです。
元のレポジトリを弄っちゃっているので、今後はqiita-cliのバージョンに追従するのが大変だなあという印象です。
XのAPIの規約がコロコロ変わるから公式では実装難しいんだろうなって作ってて思いました。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0