| Server IP : 64.225.102.56 / Your IP : 216.73.216.220 Web Server : Apache/2.4.68 (Ubuntu) mod_fcgid/2.3.9 OpenSSL/3.0.2 System : Linux smtp.zejefoy.com 5.15.0-185-generic #195-Ubuntu SMP Fri Jun 19 17:11:50 UTC 2026 x86_64 User : auto ( 1002) PHP Version : 7.4.33 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,exec,system,passthru,shell_exec,proc_open,popen MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/auto/web/cifisoa.com/public_html/ai/ |
Upload File : |
#!/usr/bin/env bash
set -euo pipefail
# Загружает JSON постов с удалённого API и публикует в WordPress через WP-CLI
# Параметры seed / variant / niche_hint проксируются в generate_post.php, чтобы темы статей не были копией друг друга.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
API_URL=""
COUNT=10
LOCALE="en_US"
WP_PATH="$PROJECT_ROOT"
FIXED_TOPIC=""
TOPIC_MODE="${WPINSTALL_TOPIC_MODE:-preset}"
usage(){
cat <<'USAGE'
Использование:
fetch_and_publish_from_api.sh --api-url URL [--count N] [--locale LOCALE] [--wp-path PATH] [--topic "TITLE"]
Пример:
FETCH_VARIANT=3 WPINSTALL_DOMAIN=example.com WPINSTALL_TOPIC_MODE=niche WPINSTALL_NICHE=travel \\
fetch_and_publish_from_api.sh --api-url "https://wpinstall.coqymoa.com/generate_post.php" --count 1 --locale en_US --wp-path /www/wwwroot/example.com/ --topic "How to pack light for a weekend trip"
USAGE
}
while [[ $# -gt 0 ]]; do
case "$1" in
--api-url) API_URL="$2"; shift 2;;
--count) COUNT="$2"; shift 2;;
--locale) LOCALE="$2"; shift 2;;
--wp-path) WP_PATH="$2"; shift 2;;
--topic) FIXED_TOPIC="$2"; shift 2;;
-h|--help) usage; exit 0;;
*) echo "Неизвестный аргумент: $1" >&2; usage; exit 1;;
esac
done
if [[ -z "$API_URL" ]]; then
echo "Нужно указать --api-url" >&2
exit 1
fi
if ! command -v wp >/dev/null 2>&1; then
echo "wp-cli не найден в PATH" >&2
exit 1
fi
pushd "$WP_PATH" >/dev/null
WP_FLAGS=""
if [[ "$(id -u)" -eq 0 ]]; then WP_FLAGS="--allow-root"; fi
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
_domain="${WPINSTALL_DOMAIN:-}"
if [[ -z "$_domain" ]]; then
_domain="$(basename "$WP_PATH" 2>/dev/null || echo "")"
fi
BASE_FETCH_VARIANT="${FETCH_VARIANT:-}"
if [[ -z "$BASE_FETCH_VARIANT" ]]; then
BASE_FETCH_VARIANT=$(( $(date +%s%N) % 997 + RANDOM % 100 ))
fi
_hints=(travel food gardening books tech wellbeing budget cooking fitness decor interviews opinion how-to local culture science design family pets career study)
_niche_hint="${WPINSTALL_NICHE:-}"
if [[ -z "$_niche_hint" ]]; then
((_hint_ix = BASE_FETCH_VARIANT % ${#_hints[@]}))
_niche_hint="${_hints[$_hint_ix]}"
fi
_enc() { php -r 'echo rawurlencode(getenv("V"));' V="$1" 2>/dev/null || printf '%s' "$1"; }
build_request_url() {
local variant="$1"
local seed="$2"
local ts="$3"
local hint="$4"
local topic="$5"
local sep='?'
[[ "$API_URL" == *\?* ]] && sep='&'
local dom_q=""
[[ -n "$_domain" ]] && dom_q="$(_enc "$_domain")"
local hint_q="$(_enc "$hint")"
local topic_q=""
[[ -n "$topic" ]] && topic_q="$(_enc "$topic")"
local mode_q="$(_enc "$TOPIC_MODE")"
local url="${API_URL}${sep}count=${COUNT}&locale=${LOCALE}"
url+="&seed=${seed}&t=${ts}&variant=${variant}"
url+="&mode=${mode_q}"
[[ -n "$dom_q" ]] && url+="&domain=${dom_q}"
url+="&niche_hint=${hint_q}"
[[ -n "$topic_q" ]] && url+="&topic=${topic_q}"
printf '%s' "$url"
}
fetch_posts() {
local variant="$1"
local hint="$2"
local out="$3"
local topic="${4:-}"
local seed ts request_url
seed="$(openssl rand -hex 14)"
ts="$(date +%s%N)"
request_url="$(build_request_url "$variant" "$seed" "$ts" "$hint" "$topic")"
if [[ -n "$topic" ]]; then
echo "↓ Запрос к API (${API_URL} … topic=${topic} locale=${LOCALE} variant=${variant})"
else
echo "↓ Запрос к API (${API_URL} … count=${COUNT} locale=${LOCALE} variant=${variant} niche_hint=${hint})"
fi
curl -fsSL "$request_url" -o "$out"
}
title_exists() {
local title="$1"
wp post list --post_type=post --post_status=any --fields=ID,post_title --format=json $WP_FLAGS | php -r '
$data=json_decode(stream_get_contents(STDIN), true) ?: [];
$needle=mb_strtolower(trim(getenv("TITLE")));
foreach($data as $row){
if(mb_strtolower(trim($row["post_title"] ?? "")) === $needle){ echo "1"; exit; }
}
' TITLE="$title" 2>/dev/null
}
fallback_image_url() {
local title="$1"
local variant="$2"
local seed_base="${_domain:-site}-${variant}-${title}"
local seed
seed="$(printf '%s' "$seed_base" | php -r 'echo substr(sha1(stream_get_contents(STDIN)), 0, 16);')"
printf 'https://picsum.photos/seed/%s/900/600.jpg' "$seed"
}
import_featured_image() {
local post_id="$1"
local img_url="$2"
local attempt_label="$3"
[[ -z "$img_url" ]] && return 1
local ct ext basename_san img_file resized att_id
CT=$(curl -fsSLI "$img_url" | awk -F': ' 'BEGIN{IGNORECASE=1} tolower($1)=="content-type"{print tolower($2); exit}' || true)
ext=""
case "$CT" in
image/jpeg* ) ext=".jpg";;
image/jpg* ) ext=".jpg";;
image/png* ) ext=".png";;
image/webp* ) ext=".webp";;
image/gif* ) ext=".gif";;
* ) ext="";;
esac
basename_san="$(basename "$img_url" | sed 's/[^A-Za-z0-9._-]/_/g')"
if [[ ! "$basename_san" =~ \.(jpg|jpeg|png|webp|gif)$ ]] && [[ -n "$ext" ]]; then
basename_san+="$ext"
fi
img_file="$TMP_DIR/${attempt_label}_${basename_san}"
if ! curl -fsSL "$img_url" -o "$img_file"; then
return 1
fi
resized="$TMP_DIR/resized_${post_id}_${attempt_label}.jpg"
if command -v convert >/dev/null 2>&1; then
convert "$img_file" -resize '768x768>' -strip -quality 82 "$resized" || true
else
SRC="$img_file" OUT="$resized" php -r '
$src=getenv("SRC"); $out=getenv("OUT"); if(!$src||!file_exists($src)){exit(0);}
$info=@getimagesize($src); if(!$info){exit(0);} $w=$info[0]; $h=$info[1]; $max=768;
if($w<=$max && $h<=$max){exit(0);} $type=$info[2];
switch($type){
case IMAGETYPE_JPEG: $im=@imagecreatefromjpeg($src); break;
case IMAGETYPE_PNG: $im=@imagecreatefrompng($src); break;
case IMAGETYPE_WEBP: $im=function_exists("imagecreatefromwebp")?@imagecreatefromwebp($src):null; break;
case IMAGETYPE_GIF: $im=@imagecreatefromgif($src); break;
default: $im=null; break;
}
if(!$im){exit(0);} $ratio=min($max/$w,$max/$h); $nw=(int)($w*$ratio); $nh=(int)($h*$ratio);
$dst=imagecreatetruecolor($nw,$nh); imagecopyresampled($dst,$im,0,0,0,0,$nw,$nh,$w,$h);
@imagejpeg($dst,$out,82);
' || true
fi
if [[ -s "$resized" ]]; then
img_file="$resized"
fi
att_id=$(wp media import "$img_file" --porcelain $WP_FLAGS | tail -n1 | tr -d '\n' || true)
if [[ -z "${att_id:-}" ]]; then
return 1
fi
wp post meta update "$post_id" _thumbnail_id "$att_id" $WP_FLAGS || return 1
}
RESP_FILE="$TMP_DIR/posts.json"
fetch_posts "$BASE_FETCH_VARIANT" "$_niche_hint" "$RESP_FILE" "$FIXED_TOPIC"
# Ожидаемый формат: { "posts": [ {"title":"...","excerpt":"...","content":"...","tags":[...],"categories":[...],"image_url":"..."}, ... ] }
POSTS_LEN=$(php -r '$_=json_decode(file_get_contents($argv[1]), true); echo is_array($_["posts"]??null)? count($_["posts"]):0;' "$RESP_FILE")
if [[ "${POSTS_LEN:-0}" -le 0 ]]; then
echo "API вернул пустой список постов" >&2
cat "$RESP_FILE" >&2 || true
exit 1
fi
ensure_category(){
local name="$1"
local term_id
term_id=$(wp term list category --fields=term_id,name,slug --format=json $WP_FLAGS | php -r '
$data=json_decode(stream_get_contents(STDIN), true) ?: [];
$name=getenv("NAME");
foreach($data as $row){
if(($row["name"]??"") === $name){ echo $row["term_id"]; exit; }
if(($row["slug"]??"") === $name){ echo $row["term_id"]; exit; }
}' NAME="$name" || true)
if [[ -n "${term_id:-}" ]]; then echo "$term_id"; return 0; fi
term_id=$(wp term create category "$name" --porcelain $WP_FLAGS | tr -d '\n' || true)
if [[ -z "${term_id:-}" ]]; then
term_id=$(wp term list category --fields=term_id,name,slug --format=json $WP_FLAGS | php -r '
$data=json_decode(stream_get_contents(STDIN), true) ?: [];
$name=getenv("NAME");
foreach($data as $row){
if(($row["name"]??"") === $name){ echo $row["term_id"]; exit; }
if(($row["slug"]??"") === $name){ echo $row["term_id"]; exit; }
}' NAME="$name" || true)
fi
if [[ -z "${term_id:-}" ]]; then echo "0"; else echo "$term_id"; fi
}
created=0
for i in $(seq 0 $((POSTS_LEN-1))); do
variant="$BASE_FETCH_VARIANT"
POST_JSON=""
for attempt in 0 1 2 3; do
if [[ "$attempt" -gt 0 ]]; then
variant=$(( BASE_FETCH_VARIANT + attempt * 1000 + i ))
if [[ -z "$FIXED_TOPIC" ]]; then
((_hint_ix = (variant + attempt) % ${#_hints[@]}))
_niche_hint="${_hints[$_hint_ix]}"
fi
fetch_posts "$variant" "$_niche_hint" "$RESP_FILE.retry" "$FIXED_TOPIC"
POST_JSON=$(php -r '$_=json_decode(file_get_contents($argv[1]), true); echo json_encode($_["posts"][0] ?? [], JSON_UNESCAPED_UNICODE);' "$RESP_FILE.retry")
else
POST_JSON=$(php -r '$_=json_decode(file_get_contents($argv[1]), true); echo json_encode($_["posts"][ (int)$argv[2] ] ?? [], JSON_UNESCAPED_UNICODE);' "$RESP_FILE" "$i")
fi
TITLE=$(printf '%s' "$POST_JSON" | php -r '$d=json_decode(stream_get_contents(STDIN), true); echo $d["title"]??"";')
if [[ -z "$TITLE" ]]; then
continue
fi
if [[ "$(title_exists "$TITLE")" == "1" ]]; then
echo "↻ Дубль заголовка, запрашиваю новый вариант: $TITLE"
continue
fi
break
done
TITLE=$(printf '%s' "$POST_JSON" | php -r '$d=json_decode(stream_get_contents(STDIN), true); echo $d["title"]??"";')
EXCERPT=$(printf '%s' "$POST_JSON" | php -r '$d=json_decode(stream_get_contents(STDIN), true); echo $d["excerpt"]??"";')
CONTENT=$(printf '%s' "$POST_JSON" | php -r '$d=json_decode(stream_get_contents(STDIN), true); echo $d["content"]??"";')
TAGS=$(printf '%s' "$POST_JSON" | php -r '$d=json_decode(stream_get_contents(STDIN), true); echo isset($d["tags"])&&is_array($d["tags"]) ? implode(",", $d["tags"]) : "";')
IMG_URL=$(printf '%s' "$POST_JSON" | php -r '$d=json_decode(stream_get_contents(STDIN), true); echo $d["image_url"]??"";')
if [[ -z "$TITLE" ]] || [[ "$(title_exists "$TITLE")" == "1" ]]; then
echo "Не удалось получить уникальный заголовок, пост пропущен." >&2
continue
fi
# Категории
CAT_JSON=$(printf '%s' "$POST_JSON" | php -r '$d=json_decode(stream_get_contents(STDIN), true); echo json_encode($d["categories"]??[], JSON_UNESCAPED_UNICODE);')
mapfile -t CAT_LIST < <(printf '%s' "$CAT_JSON" | php -r '$d=json_decode(stream_get_contents(STDIN), true); if(is_array($d)){ foreach($d as $v){ echo $v, "\n"; } }')
CAT_IDS=()
for cname in "${CAT_LIST[@]:-}"; do
cname_trimmed="$(printf '%s' "$cname" | sed 's/^\s\+//; s/\s\+$//')"
[[ -z "$cname_trimmed" ]] && continue
if [[ "$cname_trimmed" =~ ^[0-9]+$ ]]; then
if wp term get category "$cname_trimmed" $WP_FLAGS >/dev/null 2>&1; then
CAT_IDS+=("$cname_trimmed")
fi
else
cid=$(ensure_category "$cname_trimmed")
[[ "$cid" != "0" ]] && CAT_IDS+=("$cid")
fi
done
POST_ID=$(wp post create \
--post_title="$TITLE" \
--post_content="$CONTENT" \
--post_excerpt="$EXCERPT" \
--post_status=publish \
--porcelain $WP_FLAGS | tr -d '\n' || true)
if [[ -z "${POST_ID:-}" ]]; then
echo "Не удалось создать пост: $TITLE" >&2
continue
fi
if [[ ${#CAT_IDS[@]} -gt 0 ]]; then
mapfile -t CAT_IDS_UNIQ < <(printf '%s\n' "${CAT_IDS[@]}" | awk '!x[$0]++')
wp post term set "$POST_ID" category "${CAT_IDS_UNIQ[@]}" --by=id $WP_FLAGS || true
fi
if [[ -n "$TAGS" ]]; then
wp post term set "$POST_ID" post_tag "$TAGS" $WP_FLAGS || true
fi
if ! import_featured_image "$POST_ID" "$IMG_URL" "api"; then
FALLBACK_IMG_URL="$(fallback_image_url "$TITLE" "$variant")"
echo "↻ Картинка API недоступна, ставлю fallback featured image."
import_featured_image "$POST_ID" "$FALLBACK_IMG_URL" "fallback" || echo "⚠️ Не удалось поставить featured image для поста ID $POST_ID" >&2
fi
echo "Создан пост ID: $POST_ID — $TITLE"
created=$((created+1))
done
echo "Готово. Создано постов: $created из $POSTS_LEN"
popd >/dev/null