feat: AI caption generation with Claude/OpenAI (#161) #195

Merged
jmiller merged 2 commits from feature/161-ai-post-generation into dev 2026-06-28 16:23:13 +00:00
57 changed files with 459 additions and 52 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Automation
# VERSION: 01.08.45
# VERSION: 01.08.46
# BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch"
+4 -1
View File
@@ -2,6 +2,9 @@
## [Unreleased]
### Added
- **AI caption generation**: Generate platform-optimized cross-post captions from article content using Claude or OpenAI (#161)
- **AI provider config**: New "AI Caption Generation" fieldset in component options with provider, API key, model, and tone settings
- **AI Generate button**: One-click AI generation button in the Share Content panel that fills all caption fields
- **X/Twitter threads**: Auto-split messages exceeding 280 chars into reply chains at sentence boundaries
- **X/Twitter cost-optimized posting**: Optional mode to post text-only tweet first ($0.015) with URL as separate reply ($0.20)
- **X/Twitter cost warning**: Language string documenting X API pricing for text vs URL posts
@@ -89,7 +92,7 @@
## [01.03.00] --- 2026-06-21
<!-- VERSION: 01.08.45 -->
<!-- VERSION: 01.08.46 -->
All notable changes to MokoSuiteCross will be documented in this file.
+1 -1
View File
@@ -14,7 +14,7 @@
DEFGROUP: Template-Joomla
INGROUP: Template-Joomla.Documentation
REPO: https://github.com/mokoconsulting-tech/Template-Joomla/
VERSION: 01.08.45
VERSION: 01.08.46
PATH: ./CODE_OF_CONDUCT.md
BRIEF: Community expectations and enforcement guidelines
NOTE: Adapted with attribution from the Contributor Covenant v2.1
+1 -1
View File
@@ -1,6 +1,6 @@
# MokoSuiteCross
<!-- VERSION: 01.08.45 -->
<!-- VERSION: 01.08.46 -->
Cross-posting Joomla content to social media, email marketing, and chat platforms for Joomla 5/6.
+1 -1
View File
@@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla
INGROUP: Template-Joomla.Documentation
REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla
PATH: /SECURITY.md
VERSION: 01.08.45
VERSION: 01.08.46
BRIEF: Security vulnerability reporting and handling policy
-->
@@ -227,6 +227,45 @@
/>
</fieldset>
<fieldset name="ai" label="COM_MOKOSUITECROSS_CONFIG_AI">
<field
name="ai_provider"
type="list"
label="COM_MOKOSUITECROSS_CONFIG_AI_PROVIDER"
description="COM_MOKOSUITECROSS_CONFIG_AI_PROVIDER_DESC"
default="none">
<option value="none">COM_MOKOSUITECROSS_CONFIG_AI_PROVIDER_NONE</option>
<option value="claude">COM_MOKOSUITECROSS_CONFIG_AI_PROVIDER_CLAUDE</option>
<option value="openai">COM_MOKOSUITECROSS_CONFIG_AI_PROVIDER_OPENAI</option>
</field>
<field
name="ai_api_key"
type="text"
label="COM_MOKOSUITECROSS_CONFIG_AI_API_KEY"
description="COM_MOKOSUITECROSS_CONFIG_AI_API_KEY_DESC"
showon="ai_provider:claude,openai"
/>
<field
name="ai_model"
type="text"
label="COM_MOKOSUITECROSS_CONFIG_AI_MODEL"
description="COM_MOKOSUITECROSS_CONFIG_AI_MODEL_DESC"
hint="claude-haiku-4-5 / gpt-4o-mini"
showon="ai_provider:claude,openai"
/>
<field
name="ai_tone"
type="list"
label="COM_MOKOSUITECROSS_CONFIG_AI_TONE"
description="COM_MOKOSUITECROSS_CONFIG_AI_TONE_DESC"
default="professional"
showon="ai_provider:claude,openai">
<option value="professional">COM_MOKOSUITECROSS_CONFIG_AI_TONE_PROFESSIONAL</option>
<option value="friendly">COM_MOKOSUITECROSS_CONFIG_AI_TONE_FRIENDLY</option>
<option value="casual">COM_MOKOSUITECROSS_CONFIG_AI_TONE_CASUAL</option>
</field>
</fieldset>
<fieldset name="category_rules" label="COM_MOKOSUITECROSS_CONFIG_CATEGORY_RULES">
<field
name="category_rules_note"
@@ -549,6 +549,29 @@ COM_MOKOSUITECROSS_CONFIG_LINK_SHORTENER_YOURLS_URL_DESC="Full URL to your YOURL
COM_MOKOSUITECROSS_CONFIG_LINK_SHORTENER_YOURLS_TOKEN="YOURLS Signature Token"
COM_MOKOSUITECROSS_CONFIG_LINK_SHORTENER_YOURLS_TOKEN_DESC="Secret signature token from your YOURLS installation."
; AI Caption Generation
COM_MOKOSUITECROSS_CONFIG_AI="AI Caption Generation"
COM_MOKOSUITECROSS_CONFIG_AI_PROVIDER="AI Provider"
COM_MOKOSUITECROSS_CONFIG_AI_PROVIDER_DESC="Select an AI provider to generate cross-post captions from article content. The API key is stored in Joomla component params (encrypted at rest)."
COM_MOKOSUITECROSS_CONFIG_AI_PROVIDER_NONE="None (disabled)"
COM_MOKOSUITECROSS_CONFIG_AI_PROVIDER_CLAUDE="Anthropic Claude"
COM_MOKOSUITECROSS_CONFIG_AI_PROVIDER_OPENAI="OpenAI"
COM_MOKOSUITECROSS_CONFIG_AI_API_KEY="API Key"
COM_MOKOSUITECROSS_CONFIG_AI_API_KEY_DESC="API key for the selected AI provider."
COM_MOKOSUITECROSS_CONFIG_AI_MODEL="Model"
COM_MOKOSUITECROSS_CONFIG_AI_MODEL_DESC="AI model to use. Leave blank for the default (Claude Haiku 4.5 or GPT-4o Mini)."
COM_MOKOSUITECROSS_CONFIG_AI_TONE="Tone"
COM_MOKOSUITECROSS_CONFIG_AI_TONE_DESC="The writing tone for generated captions."
COM_MOKOSUITECROSS_CONFIG_AI_TONE_PROFESSIONAL="Professional"
COM_MOKOSUITECROSS_CONFIG_AI_TONE_FRIENDLY="Friendly"
COM_MOKOSUITECROSS_CONFIG_AI_TONE_CASUAL="Casual"
COM_MOKOSUITECROSS_AI_GENERATE="Generate with AI"
COM_MOKOSUITECROSS_AI_GENERATE_DESC="Generate platform-optimized captions from the article content using AI."
COM_MOKOSUITECROSS_AI_GENERATING="Generating captions..."
COM_MOKOSUITECROSS_AI_GENERATED="AI captions generated successfully."
COM_MOKOSUITECROSS_AI_ERROR="AI generation failed: %s"
COM_MOKOSUITECROSS_AI_NOT_CONFIGURED="AI is not configured. Go to Options to set up a provider and API key."
; Category Rules
COM_MOKOSUITECROSS_CONFIG_CATEGORY_RULES="Category Rules"
COM_MOKOSUITECROSS_CONFIG_CATEGORY_RULES_NOTE="Category Routing"
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="component" method="upgrade">
<name>com_mokosuitecross</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -0,0 +1 @@
/* 01.08.46 — no schema changes */
@@ -0,0 +1,100 @@
<?php
/**
* @package MokoSuiteCross
* @subpackage com_mokosuitecross
* @author Moko Consulting <hello@mokoconsulting.tech>
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
* SPDX-License-Identifier: GPL-3.0-or-later
*/
namespace Joomla\Component\MokoSuiteCross\Administrator\Controller;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Session\Session;
use Joomla\Component\MokoSuiteCross\Administrator\Helper\AiGeneratorHelper;
class AiController extends BaseController
{
public function generate(): void
{
if (!Session::checkToken('get')) {
echo json_encode(['success' => false, 'error' => 'Invalid token']);
$this->app->close();
return;
}
$user = $this->app->getIdentity();
if (!$user->authorise('core.edit', 'com_mokosuitecross')) {
echo json_encode(['success' => false, 'error' => 'Permission denied']);
$this->app->close();
return;
}
$articleId = $this->input->getInt('article_id', 0);
if ($articleId < 1) {
echo json_encode(['success' => false, 'error' => 'Missing article ID']);
$this->app->close();
return;
}
$db = Factory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName(['id', 'title', 'introtext', 'catid']))
->from($db->quoteName('#__content'))
->where($db->quoteName('id') . ' = ' . $articleId);
$db->setQuery($query);
$article = $db->loadObject();
if (!$article) {
echo json_encode(['success' => false, 'error' => 'Article not found']);
$this->app->close();
return;
}
$category = '';
$catQuery = $db->getQuery(true)
->select($db->quoteName('title'))
->from($db->quoteName('#__categories'))
->where($db->quoteName('id') . ' = ' . (int) $article->catid);
$db->setQuery($catQuery);
$category = $db->loadResult() ?: '';
$tagQuery = $db->getQuery(true)
->select($db->quoteName('t.title'))
->from($db->quoteName('#__tags', 't'))
->join('INNER', $db->quoteName('#__contentitem_tag_map', 'm') . ' ON ' . $db->quoteName('m.tag_id') . ' = ' . $db->quoteName('t.id'))
->where($db->quoteName('m.content_item_id') . ' = ' . $articleId)
->where($db->quoteName('m.type_alias') . ' = ' . $db->quote('com_content.article'));
$db->setQuery($tagQuery);
$tags = $db->loadColumn() ?: [];
$introtext = strip_tags($article->introtext ?? '');
$introtext = mb_substr($introtext, 0, 500);
$params = \Joomla\CMS\Component\ComponentHelper::getParams('com_mokosuitecross');
$config = [
'ai_provider' => $params->get('ai_provider', 'none'),
'ai_api_key' => $params->get('ai_api_key', ''),
'ai_model' => $params->get('ai_model', ''),
'ai_tone' => $params->get('ai_tone', 'professional'),
];
$result = AiGeneratorHelper::generate($article->title, $introtext, $category, $tags, $config);
$this->app->setHeader('Content-Type', 'application/json; charset=utf-8');
echo json_encode($result);
$this->app->close();
}
}
@@ -0,0 +1,196 @@
<?php
/**
* @package MokoSuiteCross
* @subpackage com_mokosuitecross
* @author Moko Consulting <hello@mokoconsulting.tech>
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
* SPDX-License-Identifier: GPL-3.0-or-later
*/
namespace Joomla\Component\MokoSuiteCross\Administrator\Helper;
defined('_JEXEC') or die;
class AiGeneratorHelper
{
public static function generate(string $title, string $introtext, string $category, array $tags, array $config): array
{
$provider = $config['ai_provider'] ?? 'none';
$apiKey = $config['ai_api_key'] ?? '';
$model = $config['ai_model'] ?? '';
$tone = $config['ai_tone'] ?? 'professional';
if ($provider === 'none' || $apiKey === '') {
return ['success' => false, 'error' => 'AI provider not configured or API key missing.'];
}
$prompt = self::buildPrompt($title, $introtext, $category, $tags, $tone);
$response = match ($provider) {
'claude' => self::callClaude($prompt, $apiKey, $model ?: 'claude-haiku-4-5'),
'openai' => self::callOpenAI($prompt, $apiKey, $model ?: 'gpt-4o-mini'),
default => '',
};
if ($response === '') {
return ['success' => false, 'error' => 'AI provider returned an empty response.'];
}
$parsed = self::parseResponse($response);
if ($parsed === null) {
return ['success' => false, 'error' => 'Could not parse AI response as JSON.'];
}
return ['success' => true, 'data' => $parsed];
}
private static function callClaude(string $prompt, string $apiKey, string $model): string
{
$payload = json_encode([
'model' => $model,
'max_tokens' => 500,
'messages' => [
['role' => 'user', 'content' => $prompt],
],
]);
$ch = curl_init('https://api.anthropic.com/v1/messages');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'x-api-key: ' . $apiKey,
'anthropic-version: 2023-06-01',
],
]);
$response = curl_exec($ch);
if ($response === false) {
curl_close($ch);
return '';
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode < 200 || $httpCode >= 300) {
return '';
}
$data = json_decode($response, true);
return $data['content'][0]['text'] ?? '';
}
private static function callOpenAI(string $prompt, string $apiKey, string $model): string
{
$payload = json_encode([
'model' => $model,
'max_tokens' => 500,
'messages' => [
['role' => 'user', 'content' => $prompt],
],
]);
$ch = curl_init('https://api.openai.com/v1/chat/completions');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . $apiKey,
],
]);
$response = curl_exec($ch);
if ($response === false) {
curl_close($ch);
return '';
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode < 200 || $httpCode >= 300) {
return '';
}
$data = json_decode($response, true);
return $data['choices'][0]['message']['content'] ?? '';
}
private static function buildPrompt(string $title, string $introtext, string $category, array $tags, string $tone): string
{
$tagList = !empty($tags) ? implode(', ', $tags) : 'none';
$toneGuide = match ($tone) {
'casual' => 'Use a relaxed, conversational tone.',
'friendly' => 'Use a warm, approachable tone with enthusiasm.',
default => 'Use a professional, polished tone.',
};
return <<<PROMPT
Generate cross-post captions for this article. {$toneGuide}
Article title: {$title}
Content summary: {$introtext}
Category: {$category}
Tags: {$tagList}
Return ONLY a JSON object with these keys (no markdown, no explanation):
{
"social": "Facebook/LinkedIn post (max 200 chars, include a call to action)",
"short": "Twitter/Bluesky post (max 270 chars, punchy, include 1-2 relevant hashtags)",
"chat": "Telegram/Discord message (max 300 chars, conversational)",
"email_subject": "Email subject line (max 60 chars, compelling, no clickbait)"
}
Rules:
- Do not include the article URL (it is added automatically)
- Do not wrap the JSON in markdown code fences
- Respect the character limits strictly
- Each caption should be unique, not just a reformatted version of the others
PROMPT;
}
private static function parseResponse(string $response): ?array
{
$response = trim($response);
if (preg_match('/\{[\s\S]*\}/', $response, $matches)) {
$response = $matches[0];
}
$data = json_decode($response, true);
if (!\is_array($data)) {
return null;
}
$required = ['social', 'short', 'chat', 'email_subject'];
foreach ($required as $key) {
if (!isset($data[$key]) || !\is_string($data[$key])) {
return null;
}
}
return [
'social' => mb_substr($data['social'], 0, 500),
'short' => mb_substr($data['short'], 0, 280),
'chat' => mb_substr($data['chat'], 0, 500),
'email_subject' => mb_substr($data['email_subject'], 0, 120),
];
}
}
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="content" method="upgrade">
<name>Content - MokoSuiteCross</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -212,8 +212,53 @@ XML;
$form->load($xml);
// Cross-post history panel for existing articles
// AI Generate button for the Share Content panel
$articleId = Factory::getApplication()->input->getInt('id', 0);
$aiParams = ComponentHelper::getParams('com_mokosuitecross');
$aiEnabled = \in_array($aiParams->get('ai_provider', 'none'), ['claude', 'openai'], true);
if ($aiEnabled && $articleId > 0) {
$aiToken = Session::getFormToken();
$aiUrl = Uri::base() . 'index.php?option=com_mokosuitecross&task=ai.generate&format=raw&article_id=' . $articleId . '&' . $aiToken . '=1';
$aiButtonHtml = '<div class="mb-3">'
. '<button type="button" id="mokosuitecross-ai-btn" class="btn btn-sm btn-outline-info" onclick="mokosuitecrossAiGenerate()">'
. '<span class="icon-magic" aria-hidden="true"></span> '
. \Joomla\CMS\Language\Text::_('COM_MOKOSUITECROSS_AI_GENERATE')
. '</button>'
. '<span id="mokosuitecross-ai-status" class="ms-2 small"></span>'
. '</div>'
. '<script>'
. 'function mokosuitecrossAiGenerate(){'
. 'var btn=document.getElementById("mokosuitecross-ai-btn");'
. 'var st=document.getElementById("mokosuitecross-ai-status");'
. 'btn.disabled=true;st.textContent="' . \Joomla\CMS\Language\Text::_('COM_MOKOSUITECROSS_AI_GENERATING', true) . '";'
. 'fetch("' . $aiUrl . '")'
. '.then(function(r){return r.json();})'
. '.then(function(d){'
. 'btn.disabled=false;'
. 'if(!d.success){st.textContent=d.error||"Error";return;}'
. 'st.textContent="' . \Joomla\CMS\Language\Text::_('COM_MOKOSUITECROSS_AI_GENERATED', true) . '";'
. 'var f=d.data;'
. 'var s=document.getElementById("jform_attribs_mokosuitecross_social_text");if(s)s.value=f.social;'
. 'var h=document.getElementById("jform_attribs_mokosuitecross_short_text");if(h)h.value=f.short;'
. 'var c=document.getElementById("jform_attribs_mokosuitecross_chat_text");if(c)c.value=f.chat;'
. 'var e=document.getElementById("jform_attribs_mokosuitecross_email_subject");if(e)e.value=f.email_subject;'
. '})'
. '.catch(function(){btn.disabled=false;st.textContent="Request failed";});'
. '}'
. '</script>';
$aiXml = '<?xml version="1.0"?>
<form><fields name="attribs"><fieldset name="mokosuitecross_share">
<field name="mokosuitecross_ai_generate" type="note"
label="" description="" />
</fieldset></fields></form>';
$form->load($aiXml);
$form->setFieldAttribute('mokosuitecross_ai_generate', 'description', $aiButtonHtml, 'attribs');
}
// Cross-post history panel for existing articles
if ($articleId > 0) {
$query = $db->getQuery(true)
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - ActivityPub (Fediverse)</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Google Blogger</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Bluesky</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Brevo (Sendinblue)</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Constant Contact</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - ConvertKit</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Dev.to</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Discord</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Facebook / Meta</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Ghost</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Google Business Profile</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Google Chat</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Hashnode</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Instagram</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-06-23</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - LinkedIn</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Mailchimp</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Mastodon</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Matrix / Element</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Medium</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - MokoSuiteCalendar Events</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - MokoSuiteGallery</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Nostr</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Ntfy Push Notifications</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Pinterest</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Reddit</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - RSS Feed</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - SendGrid</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Slack</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Microsoft Teams</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Telegram</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Threads (Meta)</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - TikTok</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Tumblr</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - X / Twitter</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Generic Webhook</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - WhatsApp Business</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - WordPress</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="mokosuitecross" method="upgrade">
<name>MokoSuiteCross - Youtube</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-06-23</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="system" method="upgrade">
<name>System - MokoSuiteCross</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="system" method="upgrade">
<name>System - MokoSuiteCross Events</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="system" method="upgrade">
<name>System - MokoSuiteCross Gallery</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="task" method="upgrade">
<name>Task - MokoSuiteCross Queue Processor</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="webservices" method="upgrade">
<name>Web Services - MokoSuiteCross</name>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
+1 -1
View File
@@ -2,7 +2,7 @@
<extension type="package" method="upgrade">
<name>MokoSuiteCross</name>
<packagename>mokosuitecross</packagename>
<version>01.08.45</version>
<version>01.08.46</version>
<creationDate>2026-05-28</creationDate>
<author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>