In this post, I’ll walk you through how to automate the creation of forum discussions using AI and the Flarum API. By integrating an AI language model, I have streamlined the process of generating relevant, engaging, and SEO-optimized discussions for my forum, making it easier to keep the community active and engaging.
API Integration: To get started, you will need the Flarum API Client. You can easily install this via SSH, similar to installing a Flarum extension. The process is well-documented, so you don't need to worry about complex configurations.
Run the following command in the root directory of your forum using SSH:
composer require maicol07/flarum-api-client
Documentation Resources:
Flarum API Client - PHP
Flarum API Client Documentation
Packagist - Flarum API Client
Setting Up the Database Table: You need to create a table in your database to store the keywords and track the progress of the posts. Below is the SQL command that will create the ai_queue table in your database. I’m using Flarum’s own database for this purpose, but you can adapt it as needed.
CREATE TABLE `ai_queue` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`title` varchar(500) NOT NULL,
`seen` int(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `title` (`title`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
COMMIT;
Create Your Flarum API Token: To authenticate your requests, you’ll need to create a token in the api_keys table of your Flarum database. A 40-character token is recommended.
For more details, refer to the Flarum REST API documentation.
Connecting to Your Database with PHP: Now that you have the database set up, let’s connect to it using PHP. In the next step, we will create two PHP files that handle database connections and content parsing. You can create the PHP file in the root directory or a subfolder of your forum.
File 1: details-txt.php
(You can change the filename, but make sure to adjust the reference in your code.)
<?php
$header = array(
"Authorization: Token <<ENTER YOUR OWN FORUM TOKEN HERE>> ",
"Content-Type: application/json"
);
// Create DB connection
$servername = "localhost";
$login = "DATABASE USERNAME";
$dbpw = "DATABASE PASSWORD";
$dbname = "DATABASE NAME";
$conn = new mysqli($servername, $login, $dbpw, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
else
{
echo "Connected to database\n";
}
?>
File 2: txtparsergemini.php
(Again, you can change the filename, but make sure to adjust the reference in your code.)
<?php
include("details-txt.php");
require 'vendor/autoload.php';
// Enable detailed error reporting for debugging
error_reporting(E_ALL);
ini_set('display_errors', 1);
// Define the API URL for content generation
$apiUrl = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-latest:generateContent?key=<<ENTER YOUR OWN API KEY HERE>>';
// Specify the path to the keywords file
$filePath = 'keywords-gemini.txt';
// Read the file line by line and check for errors
if (!file_exists($filePath) || !is_readable($filePath)) {
die("File not found or unreadable: " . $filePath);
}
$keywords = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
// Get the most recently added title from the database
$lastTitleStmt = $conn->prepare('SELECT title FROM ai_queue ORDER BY id DESC LIMIT 1');
if (!$lastTitleStmt) {
die("Database preparation error: " . $conn->error);
}
$lastTitleStmt->execute();
$lastTitleStmt->bind_result($lastTitle);
$lastTitleStmt->fetch();
$lastTitleStmt->close();
echo "Last title in the database: $lastTitle\n";
// Find the next keyword in the list after the last title
$keywordIndex = array_search($lastTitle, $keywords);
if ($keywordIndex !== false && isset($keywords[$keywordIndex + 1])) {
$keyword = $keywords[$keywordIndex + 1];
echo "Next keyword: $keyword\n";
} else {
// If the last title is not found in the keywords file, use the first keyword
$keyword = $keywords[0];
echo "First keyword: $keyword\n";
}
// Check if the keyword exists in the database before proceeding
$selectStmt = $conn->prepare('SELECT title FROM ai_queue WHERE title = ?');
$selectStmt->bind_param('s', $keyword);
$selectStmt->execute();
$selectStmt->store_result();
if ($selectStmt->num_rows === 0) {
// If the keyword is not found, send a request to the Gemini API
$prompt = "You will open a discussion on the FORUMNAME forum with the following keyword. You can use typos and abbreviations, avoid using punctuation marks. Focus on the keyword and avoid revealing that you're an AI. Use natural language, and do not use any formatting like Markdown, HTML, or tables. Please provide the response in the following format:\n\n"
. "Topic: [Title text (should be less than 75 characters)]\n"
. "Question: [Answer text]\n\n"
. "Keyword: $keyword";
// Prepare the request data in JSON format
$requestData = [
"contents" => [
[
"parts" => [
["text" => $prompt]
]
]
]
];
// cURL request
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $apiUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($requestData));
$response = curl_exec($ch);
// Error checking
if (curl_errno($ch)) {
echo "cURL Error: " . curl_error($ch) . "\n";
return;
}
$httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Print the response
echo "Gemini API Response:\n$response\n\n";
if ($httpStatus !== 200) {
echo "API Error: HTTP Status Code $httpStatus\n";
return;
}
// Process the response and extract the title and question
$responseData = json_decode($response, true);
if (isset($responseData['candidates'][0]['content']['parts'][0]['text'])) {
$geminiResponse = $responseData['candidates'][0]['content']['parts'][0]['text'];
// Extract the title and question from the response
if (preg_match('/^Topic: (.+)\nQuestion: (.+)$/s', $geminiResponse, $matches)) {
$title = trim($matches[1]);
$body = trim($matches[2]);
// Use the Flarum API to add the new topic to the forum
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://forum.siteurl.com/api/discussions');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(array(
'data' => array(
'type' => "discussions",
'attributes' => array(
'title' => "$title", // Use the title from the response
'content' => "$body" // Use the body from the response
),
'relationships' => array(
'tags' => array(
'data' => array(
array(
'type' => 'tags',
'id' => "2" // Use a fixed tag ID for a specific category
)
)
)
)
)
)));
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
$result = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpCode != 201) {
echo "HTTP Error Code: " . $httpCode . "\n";
echo "API Response: " . $result . "\n";
die("Forum API request failed.\n");
}
if (curl_errno($ch)) {
echo 'Curl error: ' . curl_error($ch) . "\n";
}
curl_close($ch);
echo "New topic created on the forum: $title\n";
// If successful, add the new record to the database
$insertStmt = $conn->prepare('INSERT INTO ai_queue (title, seen) VALUES (?, 1)');
$insertStmt->bind_param("s", $keyword);
$insertStmt->execute();
$insertStmt->close();
echo "New keyword added to the database: " . $keyword . "\n";
} else {
echo "The response from Gemini API is not in the expected format.\n";
}
} else {
echo "Could not retrieve valid content from Gemini API.\n";
}
} else {
echo "Title already exists, skipping: " . $keyword . "\n";
}
$selectStmt->close();
?>
Create a file named keywords-gemini.txt in the same directory and list one keyword per line. Do not enclose the keywords in quotation marks, and avoid adding commas at the end. You can leave spaces as needed.
Example:
keyword 1
keyword 2
keyword 3
Finally, every time you send an HTTP request to this PHP file, a new discussion will be started. Therefore, you can connect this PHP file to a CRON job using the URL method. A new discussion will be created at the desired minute.
The user who will create the discussion is the ID of the token you added in the api_keys table.
Credit: This was inspired by the following discussion: https://discuss.flarum.org/d/24017-create-posts-from-rss-feeds
This method ensures that fresh, SEO-optimized, and relevant topics are regularly posted on the forum without manual intervention.
Why This Is Useful:
Save Time: Automates the repetitive task of creating forum posts.
SEO-Friendly: The AI generates SEO-focused content.
Consistency: Posts are made regularly with minimal effort, ensuring active community engagement.
Feel free to ask questions or share your experiences with automating forum posts!