はじめに
chatGPTを使ってwordpressに自動的に投稿を作る機能が欲しいなーという想いから着手。調べたところpythonによる実装方法は普及していたが、node.jsでの実装はあまり見受けられなかったので誰かの参考になれば嬉しい。
ただし、JSの初学者がAPIの学習を兼ねて実装したもののため、冗長な記述も多々あることご承知おきいただきたい。
実装
開発環境
OS:macOS Monterey
言語:Node.js@18.17.1
モジュール:axios@1.5.0
モジュール:openai@4.3.1
本題
部品は大きく分けて2つ。openAIのAPIを利用してchatGPTにプロンプトを送り、回答をもらう部分(これが記事のタイトル、本文になる)。もう1つが、wordpressと連携して投稿を作成する処理。
chatGPTとの連携部分は下記の通り。
import OpenAI from "openai";
import * as fs from "fs";
const openai = new OpenAI({
apiKey: "sk-xxxxxxxxxxxxxxxxxxx",
});
// テキストファイルからキーワードを取得しmain処理を呼び出す
let filePath = "./keyword.txt";
fs.readFile(filePath, "utf8", async (err, data) => {
if (err) {
console.error("Error reading the file", err);
return;
}
const keyword_list = data.split("\n");
for (let keyword of keyword_list) {
let question_title =
"「" +
keyword +
"」のキーワードで上位表示するための記事のタイトルを作成してください。出力はタイトル1つのみで、文章ではなくタイトルだけでokです。";
await main(question_title);
}
});
//プロンプトを受け取り、chatGPTのAPI結果を返却する
async function generateContent(prompt) {
let content = "";
while (true) {
let i = 1;
let completion = await openai.chat.completions.create({
// chatGPTのモデルを複数定義している。使いたいモデルのコメントアウトを外す。
// model: "gpt-3.5-turbo",
// model:"gpt-4",
model: "gpt-4-0613",
messages: [
{
role: "system",
content:
"あなたはXXXXXのプロフェッショナルです。日本語で返答してください。",
},
{ role: "user", content: prompt },
],
});
let message = completion.choices[0].message.content;
content += message;
}
return content;
}
//メイン処理。fs.readFileのアロー関数で呼ばれる。
async function main(question_title) {
const generated_title = await generateContent(question_title);
console.log("タイトル:" + generated_title);
const question_content =
"「" +
generated_title +
"」のキーワードで上位表示するための記事を作成してください。次の制約を必ず守ってください。◆制約:可能な限り長いコンテンツとすること/マークダウン形式で記述すること/すぐに本文から記述すること/見出しだけでなく本文を作成すること";
const generated_content = await generateContent(question_content);
console.log("記事作成完了");
// 出力記事が確認したい場合は
// console.log(generated_content);
}
今回の機能はkeyword.txtに1行1キーワードとして、chatGPTに作成してほしいテーマを記述する。そのkeyword.txtをfsモジュールで配列に格納し、for文でぐるぐる回している。
実際にchatGPTのAPIを呼び出している処理はGenerateContentであり、引数にはpromptを持たせている。このようにすることによって、タイトル作成時と本文作成時に同様の処理で実現できる。
続けてwordpressへの投稿機能は下記の通り。
import axios from "axios";
// ここにサイトURL及びWordpressのログイン情報を保持させます
// 本来はログイン情報は別ファイルで管理が望ましいです
const wp_url = "https://junjunjp.com/xmlrpc.php";
const user = "ユーザ名";
const password = "パスワード";
// WPに記事を投稿する関数
async function createPost(generated_title, generated_content) {
const post = {
post_type: "post",
post_status: "draft",
post_title: "ここにタイトル",
post_content: "ここに本文",
};
const xml = `
<?xml version="1.0"?>
<methodCall>
<methodName>wp.newPost</methodName>
<params>
<param><value><int>1</int></value></param>
<param><value><string>${user}</string></value></param>
<param><value><string>${password}</string></value></param>
<param>
<value>
<struct>
${Object.entries(post)
.map(
([key, value]) =>
`<member>
<name>${key}</name>
<value><string>${value}</string></value>
</member>`
)
.join("")}
</struct>
</value>
</param>
</params>
</methodCall>
`;
try {
const response = await axios.post(wp_url, xml, {
headers: {
"Content-Type": "text/xml",
},
});
console.log("記事の投稿が完了しました:", response.data);
} catch (error) {
console.error("Error creating post:", error);
}
}
XML生成の部分を詳しく解説する(chatGPTより)。
1. XML の初めの部分
<?xml version="1.0"?>
<methodCall>
<methodName>wp.newPost</methodName>
<params>
この部分はXML-RPCリクエストの基本的な構造を設定している。
<?xml version="1.0"?>
: これはXMLの宣言で、このドキュメントがXML 1.0を使用していることを示す。<methodCall>
: XML-RPCリクエストの開始タグ。<methodName>wp.newPost</methodName>
: 使用するXML-RPCメソッドを指定するタグです。この場合、WordPressのwp.newPost
メソッドを使用している。<params>
: これはパラメータを含むXML-RPCリクエストの開始タグ。次にこのタグ内で渡すパラメータを定義する。
2. パラメータの部分
<param><value><int>1</int></value></param>
<param><value><string>${user}</string></value></param>
<param><value><string>${password}</string></value></param>
wp.newPost
メソッドに渡すパラメータを定義する部分。
- 1つ目の
<param>
はブログのIDを表す。WordPressのほとんどのインスタンスでは、このIDは1
。 - 2つ目の
<param>
はWordPressのユーザー名。 - 3つ目の
<param>
はそのユーザーのパスワード。
${user}
と ${password}
は、JavaScriptのテンプレート文字列の中で変数の値を埋め込むための構文。
3. 投稿のデータ部分
<param>
<value>
<struct>
${Object.entries(post).map(([key, value]) =>
`<member>
<name>${key}</name>
<value><string>${value}</string></value>
</member>`
).join('')}
</struct>
</value>
</param>
この部分は、新しい投稿のデータを含む<param>
を定義。
<struct>
: これはXML-RPCで複数のメンバーを持つデータ構造を表現するタグ。${Object.entries(post).map(([key, value]) => ...)}
: このJavaScriptの部分は、post
オブジェクトの各エントリ(キーと値のペア)を取り出し、それをXMLの<member>
形式に変換している。<name>${key}</name>
: これは投稿データのキー(例:post_type
,post_title
など)を表す。<value><string>${value}</string></value>
: これはそのキーに関連する値を表す。
この部分の結果、post
オブジェクトの各エントリはXMLの<member>
としてリストアップされる。
4. XML の終了部分
この部分はXML-RPCリクエストの終わりを示す。
</params>
</methodCall>
これらをマージしたものはgit hubに置いてありますのでよかったら見てみてください。