阿川私房教材:學程式,拿 offer!

63 個專案實戰,直接上手!
無需補習,按步驟打造你的面試作品。

立即解鎖你的轉職秘笈

長話短說

上週我建立了一個應用程式,可以使用人工智慧自動發布社交媒體貼文。我將向您展示我是如何做到的。

我們將介紹如何:

  • 將 Twitter 身份驗證新增至 Next.js 應用程式,

  • 從頭開始建立一個類似日曆的介面,

  • 使用 CopilotKit 將 AI 助理整合到軟體應用程式中,

  • 建立特定於操作的人工智慧副駕駛來處理應用程式內的各種任務,以及

  • 建立一個帖子生成器和調度應用程式。

這個專案是學習如何建立人工智慧應用程式和掌握社交媒體 API 的好方法,但不要用它來刻薄 😈

這是我,伊隆


CopilotKit:建構應用內人工智慧副駕駛的框架

CopilotKit是一個開源的AI副駕駛平台。我們可以輕鬆地將強大的人工智慧整合到您的 React 應用程式中。

建造:

  • ChatBot:上下文感知的應用內聊天機器人,可以在應用程式內執行操作 💬

  • CopilotTextArea:人工智慧驅動的文字字段,具有上下文感知自動完成和插入功能📝

  • 聯合代理:應用程式內人工智慧代理,可以與您的應用程式和使用者互動🤖

明星副駕駛套件

{% cta https://git.new/devtoarticle1 %} Star CopilotKit ⭐️ {% endcta %}


先決條件

要完全理解本教程,您需要對 React 或 Next.js 有基本的了解。

我們還將利用以下內容:

  • CopilotKit - 一個開源副駕駛框架,用於建立自訂 AI 聊天機器人、應用程式內 AI 代理程式和文字區域。

  • Redis - 用於儲存後期計劃的記憶體資料庫。

  • BullMQ - 一個 Node.js 函式庫,用於管理和處理佇列中的作業。

  • Node Cron - 一個 Node.js 函式庫,用於以特定時間間隔安排和執行任務(作業)。

  • Headless UI - 用於為應用程式建立可存取的 UI 元件。

  • X 用戶端 ID 和金鑰- 用於驗證使用者身分並代表他們建立貼文。

  • OpenAI API 金鑰- 使我們能夠使用 GPT 模型執行各種任務。


專案設定和套件安裝

首先,透過在終端機中執行以下程式碼片段來建立 Next.js 應用程式:

npx create-next-app social-media-scheduler

選擇您首選的配置設定。在本教學中,我們將使用 TypeScript 和 Next.js App Router。

建立 Next.js 應用程式

接下來,安裝專案相依性:

npm install @headlessui/react lodash bullmq ioredis node-cron

最後,安裝所需的CopilotKit 軟體套件。這些套件使我們能夠在應用程式中使用 AI 自動完成功能,讓 AI copilot 從 React 狀態檢索資料,並在應用程式中做出決策。

npm install @copilotkit/react-ui @copilotkit/react-textarea @copilotkit/react-core @copilotkit/backend

恭喜!您現在已準備好建立應用程式。


使用 Next.js 建立帖子調度程序應用程式

在本節中,您將了解如何為調度應用程式建立使用者介面。該應用程式分為兩個頁面:登入頁面和儀表板頁面,使用者可以在其中建立和安排貼文。

登入頁面使用 X (Twitter) 個人資料對使用者進行身份驗證,而儀表板頁面允許使用者建立、刪除和安排貼文。

登入頁面

登入頁面代表應用程式的主頁。用戶需要使用 Twitter 帳戶登入才能存取儀表板。

若要實現此目的,請更新page.tsx檔案以顯示登入按鈕,如下所示:

import Link from "next/link";
import { getTwitterOauthUrl } from "@/app/util";

export default function Home() {
    return (
        <main className='w-full min-h-screen flex flex-col items-center justify-center p-8'>
            <h2 className='font-semibold text-2xl mb-4'>Your AI Post Scheduler</h2>
            <Link
                href={getTwitterOauthUrl()}
                className='bg-black py-3 px-6 hover:bg-gray-700 text-gray-50 rounded-lg'
            >
                Sign in with Twitter
            </Link>
        </main>
    );
}

上面的程式碼片段顯示了一個Sign in with Twitter按鈕,該按鈕將用戶重新導向到 Twitter Oauth2 頁面。您很快就會了解如何設定 Twitter 身份驗證。

登入按鈕

儀表板頁面

在繼續之前,請在 Next.js 專案的根目錄中建立一個types.d.ts檔案。該文件將包含應用程式中變數的類型聲明。

interface DelSelectedCell {
    content?: string;
    day_id?: number;
    day?: string;
    time_id?: number;
    time?: string;
    published?: boolean;
    minutes?: number;
}
interface SelectedCell {
    day_id?: number;
    day?: string;
    time_id?: number;
    time?: string;
    minutes?: number;
}

interface Content {
    minutes?: number;
    content?: string;
    published?: boolean;
    day?: number;
}

interface AvailableScheduleItem {
    time: number;
    schedule: Content[][];
}

在 Next.js 應用程式資料夾中建立一個utils文件,並將此程式碼片段從 GitHub 儲存庫複製到其中。它包含在應用程式中執行各種資料操作所需的函數。

接下來,在 Next.js 應用程式目錄中建立一個包含page.tsx檔案的dashboard資料夾。

cd app
mkdir dashboard && cd dashboard
touch page.tsx

將下面的程式碼片段複製到dashboard/page.tsx檔案中。它渲染一個App元件,該元件接受應用程式的時間表作為 props 並將它們顯示在表格中:

"use client";
import _ from "lodash";
import { useState } from "react";
import App from "@/app/components/App";
import { availableSchedule } from "../util";

export default function Dashboard() {
    //👇🏻 saves a deep copy of the availableSchedule array into the React state
    const [yourSchedule, updateYourSchedule] = useState<AvailableScheduleItem[]>(
        _.cloneDeep(availableSchedule)
    );

    return (
        <App yourSchedule={yourSchedule} updateYourSchedule={updateYourSchedule} />
    );
}

後期時間表

這是上表的資料結構:

export const tableHeadings: string[] = [
    "Time",
    "Sunday",
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday",
];

export const availableSchedule: AvailableScheduleItem[] = [
    {
        time: 0,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 1,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 2,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 3,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 4,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 5,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 6,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 7,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 8,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 9,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 10,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 11,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 12,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 13,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 14,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 15,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 16,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 17,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 18,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 19,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 20,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 21,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 22,
        schedule: [[], [], [], [], [], [], []],
    },
    {
        time: 23,
        schedule: [[], [], [], [], [], [], []],
    },
];

tableHeadings陣列包含表格列的標題,而availableSchedule陣列包含一組物件。每個物件都有一個代表一天中每個小時的time屬性和一個包含嵌套陣列的schedule屬性,每個元素代表一周中的一天。

例如,當使用者設定星期三上午 8 點的計劃時,應用程式會搜尋time屬性為 8 的物件,並透過將計劃插入嵌套陣列的第四個索引處來更新其schedule屬性。

{% 嵌入 https://youtu.be/Vn-tuKgqMp4 %}

您可以從GitHub 儲存庫複製儀表板頁面的其餘 UI 元素。

在接下來的部分中,您將了解如何將 Twitter OAuth 和 CopilotKit 新增至應用程式。


如何將 X 身份驗證新增至您的 Next.js 應用程式

在本部分中,您將了解如何建立 X Developer 專案並向您的 Next.js 應用程式新增 X 驗證。

確保您擁有 X 帳戶並存取X 開發人員入口網站以建立新專案。

建立 X 開發者專案

輸入專案名稱並提供建立新專案和應用程式所需問題的答案。

X 開發者專案

設定使用者身份驗證設定以允許您代表使用者讀取和寫入貼文。

X 身份驗證設定

最後,相應地填寫App info部分。

X 應用程式資訊部分

設定身份驗證流程後,將 OAuth 2.0 用戶端 ID 和金鑰儲存到.env.local檔案中。

TWITTER_CLIENT_ID=<your_client_ID>
NEXT_PUBLIC_TWITTER_CLIENT_ID=<your_client_ID>
TWITTER_CLIENT_SECRET=<your_client_Secret>

透過 X 對使用者進行身份驗證

在 Next.js app資料夾中建立一個api資料夾。在 api 資料夾內,建立一個包含route.ts檔案的twitter目錄。這將建立一個 API 端點 ( /api/twitter ),使我們能夠對使用者進行身份驗證。

cd app
mkdir api && cd api
mkdir twitter && cd twitter
touch route.ts

將下面的程式碼片段複製到route.ts檔中:

import { NextRequest, NextResponse } from "next/server";

const BasicAuthToken = Buffer.from(
    `${process.env.TWITTER_CLIENT_ID!}:${process.env.TWITTER_CLIENT_SECRET!}`,
    "utf8"
).toString("base64");

const twitterOauthTokenParams = {
    client_id: process.env.TWITTER_CLIENT_ID!,
    code_verifier: "8KxxO-RPl0bLSxX5AWwgdiFbMnry_VOKzFeIlVA7NoA",
    redirect_uri: `http://www.localhost:3000/dashboard`,
    grant_type: "authorization_code",
};

//👇🏻 gets user access token
export const fetchUserToken = async (code: string) => {
    try {
        const formatData = new URLSearchParams({
            ...twitterOauthTokenParams,
            code,
        });
        const getTokenRequest = await fetch(
            "https://api.twitter.com/2/oauth2/token",
            {
                method: "POST",
                body: formatData.toString(),
                headers: {
                    "Content-Type": "application/x-www-form-urlencoded",
                    Authorization: `Basic ${BasicAuthToken}`,
                },
            }
        );
        const getTokenResponse = await getTokenRequest.json();
        return getTokenResponse;
    } catch (err) {
        return null;
    }
};

//👇🏻gets user's data from the access token
export const fetchUserData = async (accessToken: string) => {
    try {
        const getUserRequest = await fetch("https://api.twitter.com/2/users/me", {
            headers: {
                "Content-type": "application/json",
                Authorization: `Bearer ${accessToken}`,
            },
        });
        const getUserProfile = await getUserRequest.json();
        return getUserProfile;
    } catch (err) {
        return null;
    }
};

//👉🏻 API endpoint utilizing the functions above
  • 從上面的程式碼片段來看,
- The `BasicAuthToken` variable contains the encoded version of your tokens.
- The `twitterOauthTokenParams` contains the parameters required for getting the users' access token.
- The `fetchUserToken` function sends a request containing a code to Twitter's endpoint and returns the user's access token.
- The `fetchUserData` function uses the token to retrieve the user's X profile.

將此端點加入函數下方。當使用者登入時,它接受來自前端的程式碼,並將使用者 ID、使用者名稱和存取權令牌儲存在檔案中,在伺服器上執行作業時可以存取該檔案。

import { writeFile } from "fs";

export async function POST(req: NextRequest) {
    const { code } = await req.json();
    try {
        //👇🏻 get access token and the entire response
        const tokenResponse = await fetchUserToken(code);
        const accessToken = await tokenResponse.access_token;
        //👇🏻 get user data
        const userDataResponse = await fetchUserData(accessToken);
        const userCredentials = { ...tokenResponse, ...userDataResponse };

        //👇🏻  merge the user's access token, id, and username into an object
        const userData = {
            accessToken: userCredentials.access_token,
            _id: userCredentials.data.id,
            username: userCredentials.data.username,
        };
        //👇🏻 store them in a JSON file (for server-use)
        writeFile("./src/user.json", JSON.stringify(userData, null, 2), (error) => {
            if (error) {
                console.log("An error has occurred ", error);
                throw error;
            }
            console.log("Data written successfully to disk");
        });
        //👇🏻 returns a successful response
        return NextResponse.json(
            {
                data: "User data stored successfully",
            },
            { status: 200 }
        );
    } catch (err) {
        return NextResponse.json({ error: err }, { status: 500 });
    }
}

更新dashboard/page.tsx以在對使用者進行身份驗證後將程式碼傳送到API端點。

import { useSearchParams } from 'next/navigation'
const searchParams = useSearchParams()
    const code = searchParams.get('code')

    const fetchToken = useCallback(async () => { 
            const res = await fetch("/api/twitter", {
                method: "POST",
                body: JSON.stringify({ code }),
                headers: {
                    "Content-Type": "application/json",
                },
            });
            if (res.ok) {
                const data = await res.json();
                console.log(data);
            }
    }, [code]);

    useEffect(() => {
        fetchToken();
    }, [fetchToken]);

恭喜!當用戶點擊Sign in with Twitter按鈕時,會將他們重新導向到 Twitter 授權頁面以允許他們存取應用程式。

X 身份驗證預覽


如何將 CopilotKit 新增至 Next.js 應用程式

在本部分中,您將了解如何將 CopilotKit 加入到應用程式,以使用戶能夠使用 AI copilot 自動安排帖子,並在建立帖子內容時加入自動完成功能。

在繼續之前,請造訪OpenAI 開發者平台並建立一個新的金鑰。

開放人工智慧API

建立一個.env.local檔案並將新建立的金鑰複製到該檔案中。

OPENAI_API_KEY=<YOUR_OPENAI_SECRET_KEY>
OPENAI_MODEL=gpt-4-1106-preview

接下來,您需要為 CopilotKit 建立 API 端點。在 Next.js 應用程式資料夾中,建立一個包含route.ts檔案的api/copilotkit資料夾。

cd app
mkdir api && cd api
mkdir copilotkit && cd copilotkit
touch route.ts

將下面的程式碼片段複製到route.ts 檔案中。 CopilotKit 後端接受使用者的請求並使用 OpenAI 模型做出決策。

import { CopilotRuntime, OpenAIAdapter } from "@copilotkit/backend";

export const runtime = "edge";

export async function POST(req: Request): Promise<Response> {
    const copilotKit = new CopilotRuntime({});
    const openaiModel = process.env["OPENAI_MODEL"];

    return copilotKit.response(req, new OpenAIAdapter({model: openaiModel}));
}

若要將應用程式連接到後端 API 路由,請將下面的程式碼片段複製到dashboard/page.tsx檔案中。

"use client";
import App from "@/app/components/App";
import _ from "lodash";
import {  useState } from "react";
import { availableSchedule } from "../util";

//👇🏻 CopilotKit components
import { CopilotKit } from "@copilotkit/react-core";
import { CopilotPopup } from "@copilotkit/react-ui";

//👇🏻 CSS styles for CopilotKit components
import "@copilotkit/react-ui/styles.css";
import "@copilotkit/react-textarea/styles.css";

export default function Dashboard() {
    const [yourSchedule, updateYourSchedule] = useState<AvailableScheduleItem[]>(
        _.cloneDeep(availableSchedule)
    );

    //👉🏻 other UI states and functions

    return (
        <CopilotKit runtimeUrl='/api/copilotkit/'>
            <App
                yourSchedule={yourSchedule}
                updateYourSchedule={updateYourSchedule}
            />
            <CopilotPopup
                instructions='Help the user create and manage ad campaigns.'
                defaultOpen={true}
                labels={{
                    title: "Posts Scheduler Copilot",
                    initial:
                        "Hello there! I can help you manage your schedule. What do you want to do? You can generate posts, add, and delete scheduled posts.",
                }}
                clickOutsideToClose={false}
            ></CopilotPopup>
         </CopilotKit>
    );
}

CopilotKit元件包裝整個應用程式並接受包含 API 端點連結的runtimeUrl屬性。 CopilotKitPopup元件為應用程式加入了一個聊天機器人側邊欄面板,使我們能夠向 CopilotKit 提供各種指令。

應用程式概述


如何使用 CopilotKit 安排帖子

CopilotKit 提供了兩個鉤子,使我們能夠處理使用者的請求並插入應用程式狀態: useCopilotActionuseCopilotReadable

useCopilotAction掛鉤可讓您定義 CopilotKit 執行的動作。它接受包含以下參數的物件:

  • name - 操作的名稱。

  • 描述 - 操作的描述。

  • 參數 - 包含所需參數清單的陣列。

  • render - 預設的自訂函數或字串。

  • handler - 由操作觸發的可執行函數。

useCopilotAction({
    name: "sayHello",
    description: "Say hello to someone.",
    parameters: [
        {
            name: "name",
            type: "string",
            description: "name of the person to say greet",
        },
    ],
    render: "Process greeting message...",
    handler: async ({ name }) => {
        alert(`Hello, ${name}!`);
    },
});

useCopilotReadable掛鉤向 CopilotKit 提供應用程式狀態。

import { useCopilotReadable } from "@copilotkit/react-core";

const myAppState = "...";
useCopilotReadable({
  description: "The current state of the app",
  value: myAppState
});

現在,讓我們將應用程式狀態插入 CopilotKit 並建立一個幫助我們安排貼文的操作。

App元件中,將schedule狀態傳遞到 CopilotKit。您還可以提供附加資訊(上下文)以使 CopilotKit 做出充分且準確的決策。

//👇🏻 Application state
useCopilotReadable({
    description: "The user's Twitter post schedule",
    value: yourSchedule,
});

//👇🏻 Application context
useCopilotReadable({
    description: "Guidelines for the user's Twitter post schedule",
    value:
            "Your schedule is displayed in a table format. Each row represents an hour of the day, and each column represents a day of the week. You can add a post by clicking on an empty cell, and delete a post by clicking on a filled cell. Sunday is the first day of the week and has a day_id of 0.",
    });

建立一個 CopilotKit 操作,根據使用者的提示安排貼文:

    useCopilotAction({
        name: "updatePostSchedule",
        description: "Update the user's Twitter post schedule",
        parameters: [
            {
                name: "update_schedule",
                type: "object",
                description: "The user's updated post schedule",
                attributes: [
                    {
                        name: "time",
                        type: "number",
                        description: "The time of the post",
                    },
                    {
                        name: "schedule",
                        type: "object[]",
                        description: "The schedule for the time",
                        attributes: [
                            {
                                name: "content",
                                type: "string",
                                description: "The content of the post",
                            },
                            {
                                name: "minutes",
                                type: "number",
                                description: "The minutes past the hour",
                            },
                            {
                                name: "published",
                                type: "boolean",
                                description: "Whether the post is published",
                            },
                            {
                                name: "day",
                                type: "number",
                                description: "The day of the week",
                            },
                        ],
                    },
                ],
            },
        ],
        handler: ({ update_schedule }) => {
            setAddEventModal(true);
            setSelectedCell({
                day_id: update_schedule.schedule[0].day + 1,
                day: tableHeadings[update_schedule.schedule[0].day + 1],
                time_id: update_schedule.time,
                time: formatTime(update_schedule.time),
            });
            setContent(update_schedule.schedule[0].content);
            setMinute(update_schedule.schedule[0].minutes);
        },
        render: "Updating schedule...",
    });

上面的程式碼片段顯示了正在執行的useCopilotAction掛鉤。它接受一個包含namedescriptionparametershandlerrender屬性的物件。

  • name屬性表示操作的名稱。

  • description屬性提供了函數功能的簡要概述。

  • parameters陣列包含一個帶有timeschedule屬性的update_schedule物件。 schedule物件包括contentminutespublishedday屬性。

  • handler函數描述了觸發時要執行的操作。在上面的範例中, handler函數開啟AddPost模式,使用 AI 產生的輸入更新其值,並允許使用者相應地調整時間表。

{% 嵌入 https://youtu.be/ZJ7kcpfXyZc %}

使用 Redis 和 BullMQ 管理和安排帖子

在本節中,您將了解如何將發布計劃儲存在 Redis 資料庫中,並建立作業來定期檢查計劃以在 X (Twitter) 上發佈內容。

首先,您需要在電腦上安裝Redis 。如果您使用的是 MacOS 並安裝了Homebrew ,請在終端機中執行程式碼片段來安裝 Redis:

brew --version
brew install redis

安裝過程完成後,您可以透過在終端機中執行以下程式碼片段來測試您的 Redis 伺服器:

redis-server

Redis伺服器

現在,您可以在應用程式中使用Node.js Redis 用戶端

在伺服器上建立一個/api/schedule API 路由,當使用者新增或刪除預定貼文時,該路由會接受整個時間表。

import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) { 
    const { schedule } = await req.json();
    try {
         console.log({schedule})

        return NextResponse.json(
            { message: "Schedule updated!", schedule },
            { status: 200 }
        );

    } catch (error) { 
        return NextResponse.json(
            { message: "Error updating schedule", error },
            { status: 500 }
        );

    }   
}

更新 API 端點以將整個計劃儲存在 Redis 資料庫中。 Redis 以鍵/值對的形式儲存資料,使其儲存和檢索資料的速度超快。

import { NextRequest, NextResponse } from "next/server";
import Redis from "ioredis";
const redis = new Redis();

export async function POST(req: NextRequest) { 
    const { schedule } = await req.json();
    try {
        //👇🏻 saves the schedule
        await redis.set("schedule", JSON.stringify(schedule));

        return NextResponse.json(
            { message: "Schedule updated!", schedule },
            { status: 200 }
        );
    } catch (error) { 
        return NextResponse.json(
            { message: "Error updating schedule", error },
            { status: 500 }
        );
    }
}

您也可以在api/schedule/route.ts檔案中新增 GET 請求處理程序,以從 Redis 資料庫取得現有的計畫帖子,並在使用者登入應用程式時顯示它們。

export async function GET() {
    try {
        const schedule = await redis.get("schedule");
        if (schedule) {
            return NextResponse.json(
                { message: "Schedule found", schedule: JSON.parse(schedule) },
                { status: 200 }
            );
        }
    } catch (error) {
        return NextResponse.json(
        { message: "Schedule not found" },
        { status: 500 }
    );
}
}

最後,您需要設定一個每分鐘執行的作業佇列,以檢查當天安排的貼文並在適當的時間發布它們。

在 Next.js src資料夾中建立一個worker.ts檔案並將以下程式碼複製到該檔案中:

import data from "./user.json";
import { Worker, Queue } from 'bullmq';
import Redis from "ioredis";

//👇🏻 initializes a job queue connected to the Redis database
const redis = new Redis({maxRetriesPerRequest: null});
const scheduleQueue = new Queue('schedule-queue', { connection: redis });

上面的程式碼片段建立了一個連接到 Redis 資料庫的作業佇列。

worker.ts檔案中實作scheduleJobs函數,取得目前時間安排的貼文並將它們加入作業佇列中。

//👇🏻 add jobs to the queue
export const scheduleJobs = async (schedule: AvailableScheduleItem[]) => {
    //👇🏻 gets current time and day
    const now = new Date();
    const currentHour = now.getHours();
    const currentMinute = now.getMinutes();
    const currentDay = now.getDay();

    //👇🏻 gets posts for the current hour
    const currentSchedule = schedule.find((item) => item.time === currentHour);
    const schedulesForTheHour = currentSchedule?.schedule[currentDay];

    //👇🏻 gets scheduled posts for the current time
    if (schedulesForTheHour && schedulesForTheHour?.length > 0) {
        const awaitingJobs = schedulesForTheHour.filter(
            (scheduleItem) =>
                scheduleItem.minutes && scheduleItem.minutes <= currentMinute
        );

        //👇🏻 add jobs to queue
        return awaitingJobs.map(async (scheduleItem) => {
            const job = await scheduleQueue.add("jobs", {
                message: scheduleItem.content
            }, {
                removeOnComplete: true,
            });
            console.log(`Job ${job.id} added to queue`);
        });
    }
};

scheduleJobs函數匯入到api/schedule端點,並使用Node Cron每分鐘觸發該函數。

//👉🏻 api/schedule/route.ts
import cron from "node-cron";

export async function POST(req: NextRequest) { 
    const { schedule } = await req.json();
    try {
        await redis.set("schedule", JSON.stringify(schedule));
        cron.schedule('* * * * *', async() => {
            console.log('Triggering jobs...');
            await scheduleJobs(schedule);
        });

        return NextResponse.json(
            { message: "Schedule updated!", schedule },
            { status: 200 }
        );
    } catch (error) { 
        return NextResponse.json(
            { message: "Error updating schedule", error },
            { status: 500 }
        );
    }
}

接下來,在workers.ts檔案中新增一個工作函數,該函數透過將貼文內容傳送到X (Twitter) 來執行佇列中的作業。

//👇🏻 processing jobs
const scheduleWorker = new Worker('schedule-queue', async (job) => {
  console.log(`Processing job ${job.id} of type ${job.name} with data: ${job.data.message}`)
    console.log("Posting content...")

    //👇🏻 post content to X
    const postTweet = await fetch("https://api.twitter.com/2/tweets", {
        method: "POST",
        headers: {
            "Content-type": "application/json",
            Authorization: `Bearer ${data.accessToken}`,
        },
        body: JSON.stringify({ text: job.data.message })
    });
    if (postTweet.ok) { 
          console.log("Content posted!")
    }
}, { connection: redis})

//👇🏻 listening for completed job
scheduleWorker.on('completed', job => {
    console.log(`${job.id} has completed!`);
});

最後,您可以在更新package.json檔案中的腳本後執行npm run worker來執行工作程式。

{
 "scripts": {
    "worker": "npx tsx --watch src/worker.ts"
  }
}

恭喜!您已完成本教學的專案。


結論

到目前為止,您已經學習如何透過X 對使用者進行身份驗證、將資料儲存在Redis 資料庫中、使用Redis 和BullMQ 建立和管理作業,以及使用CopilotKit 將AI 助理整合到您的Next.js 應用程式中。

CopilotKit是一款令人難以置信的工具,可讓您在幾分鐘內將 AI Copilot 加入到您的產品中。無論您是對人工智慧聊天機器人和助理感興趣,還是對複雜任務的自動化感興趣,都可以簡化流程。

如果您需要建立 AI 產品或將 AI 工具整合到您的軟體應用程式中,您應該考慮 CopilotKit。

您可以在 GitHub 上找到本教學的源程式碼:

https://github.com/dha-stix/ai-post-generator-and-scheduler-with-copilotkit

感謝您的閱讀!


原文出處:https://dev.to/github20k/i-10xd-my-x-posts-using-the-twitter-api-openai-copilotkit-2d5n


共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。

阿川私房教材:學程式,拿 offer!

63 個專案實戰,直接上手!
無需補習,按步驟打造你的面試作品。

立即解鎖你的轉職秘笈