Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2080375343 | |||
| 8e748c2072 |
@@ -5,7 +5,7 @@
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Automation
|
||||
# VERSION: 01.08.17
|
||||
# VERSION: 01.08.18
|
||||
# BRIEF: Auto-create feature branch when an issue is opened
|
||||
|
||||
name: "Universal: Issue Branch"
|
||||
|
||||
+4
-1
@@ -2,6 +2,9 @@
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- **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
|
||||
- **Instagram carousel**: Multi-image/video posts via Meta carousel container flow (up to 10 items)
|
||||
- **Instagram Reels**: Short-form video publishing via REELS media type
|
||||
- **Instagram Stories**: Image and video story publishing via STORIES media type
|
||||
@@ -82,7 +85,7 @@
|
||||
## [01.03.00] --- 2026-06-21
|
||||
|
||||
|
||||
<!-- VERSION: 01.08.17 -->
|
||||
<!-- VERSION: 01.08.18 -->
|
||||
|
||||
All notable changes to MokoSuiteCross will be documented in this file.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# MokoSuiteCross
|
||||
|
||||
<!-- VERSION: 01.08.17 -->
|
||||
<!-- VERSION: 01.08.18 -->
|
||||
|
||||
Cross-posting Joomla content to social media, email marketing, and chat platforms for Joomla 5/6.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension type="component" method="upgrade">
|
||||
<name>com_mokosuitecross</name>
|
||||
<version>01.08.17</version>
|
||||
<version>01.08.18</version>
|
||||
<creationDate>2026-05-28</creationDate>
|
||||
<author>Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/* 01.08.18 — no schema changes */
|
||||
@@ -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.17</version>
|
||||
<version>01.08.18</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 - ActivityPub (Fediverse)</name>
|
||||
<version>01.08.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</version>
|
||||
<creationDate>2026-05-28</creationDate>
|
||||
<author>Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
|
||||
+1
@@ -1,2 +1,3 @@
|
||||
PLG_MOKOSUITECROSS_TWITTER="MokoSuiteCross - X / Twitter"
|
||||
PLG_MOKOSUITECROSS_TWITTER_DESCRIPTION="Cross-post Joomla articles to X / Twitter."
|
||||
PLG_MOKOSUITECROSS_TWITTER_COST_WARNING="X API Pricing: Text-only posts cost $0.015 each. Posts containing URLs cost $0.20 each. Cross-posting articles with links will incur URL post charges."
|
||||
|
||||
@@ -51,9 +51,6 @@ class TwitterService extends CMSPlugin implements SubscriberInterface, MokoSuite
|
||||
|
||||
public function publish(string $message, array $media, array $credentials, array $params): array
|
||||
{
|
||||
$apiUrl = 'https://api.twitter.com/2/tweets';
|
||||
$postData = json_encode(['text' => mb_substr($message, 0, 280)]);
|
||||
|
||||
$consumerKey = $credentials['api_key'] ?? '';
|
||||
$consumerSecret = $credentials['api_secret'] ?? '';
|
||||
$accessToken = $credentials['access_token'] ?? '';
|
||||
@@ -67,41 +64,17 @@ class TwitterService extends CMSPlugin implements SubscriberInterface, MokoSuite
|
||||
];
|
||||
}
|
||||
|
||||
$authHeader = $this->buildOAuth1Header('POST', $apiUrl, $consumerKey, $consumerSecret, $accessToken, $tokenSecret);
|
||||
|
||||
$ch = curl_init($apiUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $postData,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Content-Type: application/json',
|
||||
'Authorization: ' . $authHeader,
|
||||
],
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if ($response === false) {
|
||||
|
||||
$curlError = curl_error($ch);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
return ['success' => false, 'platform_post_id' => '', 'response' => ['error' => 'Connection error: ' . $curlError]];
|
||||
|
||||
}
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
$data = json_decode($response, true) ?: [];
|
||||
|
||||
if ($httpCode === 201 && !empty($data['data']['id'])) {
|
||||
return ['success' => true, 'platform_post_id' => $data['data']['id'], 'response' => $data];
|
||||
if (!empty($params['cost_optimize'])) {
|
||||
return $this->publishCostOptimized($message, $credentials, $consumerKey, $consumerSecret, $accessToken, $tokenSecret);
|
||||
}
|
||||
|
||||
return ['success' => false, 'platform_post_id' => '', 'response' => $data];
|
||||
$chunks = $this->splitIntoThread($message);
|
||||
|
||||
if (\count($chunks) === 1) {
|
||||
return $this->postTweet($chunks[0], null, $consumerKey, $consumerSecret, $accessToken, $tokenSecret);
|
||||
}
|
||||
|
||||
return $this->postThread($chunks, $consumerKey, $consumerSecret, $accessToken, $tokenSecret);
|
||||
}
|
||||
|
||||
public function validateCredentials(array $credentials): array
|
||||
@@ -158,6 +131,201 @@ class TwitterService extends CMSPlugin implements SubscriberInterface, MokoSuite
|
||||
return true;
|
||||
}
|
||||
|
||||
private function splitIntoThread(string $message, int $maxLen = 280): array
|
||||
{
|
||||
if (mb_strlen($message) <= $maxLen) {
|
||||
return [$message];
|
||||
}
|
||||
|
||||
$chunks = [];
|
||||
|
||||
while (mb_strlen($message) > $maxLen) {
|
||||
$segment = mb_substr($message, 0, $maxLen);
|
||||
|
||||
$splitPos = false;
|
||||
|
||||
foreach (['. ', '! ', '? '] as $delimiter) {
|
||||
$pos = mb_strrpos($segment, $delimiter);
|
||||
|
||||
if ($pos !== false && ($splitPos === false || $pos > $splitPos)) {
|
||||
$splitPos = $pos + mb_strlen($delimiter) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($splitPos === false || $splitPos < 1) {
|
||||
$splitPos = mb_strrpos($segment, ' ');
|
||||
}
|
||||
|
||||
if ($splitPos === false || $splitPos < 1) {
|
||||
$splitPos = $maxLen;
|
||||
}
|
||||
|
||||
$chunks[] = trim(mb_substr($message, 0, $splitPos));
|
||||
$message = trim(mb_substr($message, $splitPos));
|
||||
}
|
||||
|
||||
if ($message !== '') {
|
||||
$chunks[] = $message;
|
||||
}
|
||||
|
||||
return $chunks;
|
||||
}
|
||||
|
||||
private function postTweet(
|
||||
string $text,
|
||||
?string $replyToId,
|
||||
string $consumerKey,
|
||||
string $consumerSecret,
|
||||
string $accessToken,
|
||||
string $tokenSecret
|
||||
): array {
|
||||
$apiUrl = 'https://api.twitter.com/2/tweets';
|
||||
|
||||
$body = ['text' => $text];
|
||||
|
||||
if ($replyToId !== null) {
|
||||
$body['reply'] = ['in_reply_to_tweet_id' => $replyToId];
|
||||
}
|
||||
|
||||
$postData = json_encode($body);
|
||||
$authHeader = $this->buildOAuth1Header('POST', $apiUrl, $consumerKey, $consumerSecret, $accessToken, $tokenSecret);
|
||||
|
||||
$ch = curl_init($apiUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $postData,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Content-Type: application/json',
|
||||
'Authorization: ' . $authHeader,
|
||||
],
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if ($response === false) {
|
||||
$curlError = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return ['success' => false, 'platform_post_id' => '', 'response' => ['error' => 'Connection error: ' . $curlError]];
|
||||
}
|
||||
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
$data = json_decode($response, true) ?: [];
|
||||
|
||||
if ($httpCode === 201 && !empty($data['data']['id'])) {
|
||||
return ['success' => true, 'platform_post_id' => $data['data']['id'], 'response' => $data];
|
||||
}
|
||||
|
||||
return ['success' => false, 'platform_post_id' => '', 'response' => $data];
|
||||
}
|
||||
|
||||
private function postThread(
|
||||
array $chunks,
|
||||
string $consumerKey,
|
||||
string $consumerSecret,
|
||||
string $accessToken,
|
||||
string $tokenSecret
|
||||
): array {
|
||||
$firstResult = $this->postTweet($chunks[0], null, $consumerKey, $consumerSecret, $accessToken, $tokenSecret);
|
||||
|
||||
if (!$firstResult['success']) {
|
||||
return $firstResult;
|
||||
}
|
||||
|
||||
$rootId = $firstResult['platform_post_id'];
|
||||
$previousId = $rootId;
|
||||
|
||||
for ($i = 1, $count = \count($chunks); $i < $count; $i++) {
|
||||
$result = $this->postTweet($chunks[$i], $previousId, $consumerKey, $consumerSecret, $accessToken, $tokenSecret);
|
||||
|
||||
if (!$result['success']) {
|
||||
return [
|
||||
'success' => false,
|
||||
'platform_post_id' => $rootId,
|
||||
'response' => [
|
||||
'error' => 'Thread failed at tweet ' . ($i + 1) . ' of ' . $count,
|
||||
'root_tweet' => $rootId,
|
||||
'failed_tweet' => $result['response'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$previousId = $result['platform_post_id'];
|
||||
}
|
||||
|
||||
return ['success' => true, 'platform_post_id' => $rootId, 'response' => $firstResult['response']];
|
||||
}
|
||||
|
||||
private function publishCostOptimized(
|
||||
string $message,
|
||||
array $credentials,
|
||||
string $consumerKey,
|
||||
string $consumerSecret,
|
||||
string $accessToken,
|
||||
string $tokenSecret
|
||||
): array {
|
||||
$urlPattern = '/https?:\/\/\S+/';
|
||||
$urls = [];
|
||||
preg_match_all($urlPattern, $message, $urls);
|
||||
$urls = $urls[0] ?? [];
|
||||
|
||||
$textOnly = trim(preg_replace($urlPattern, '', $message));
|
||||
$textOnly = preg_replace('/\s{2,}/', ' ', $textOnly);
|
||||
|
||||
if ($textOnly === '' && $urls === []) {
|
||||
return ['success' => false, 'platform_post_id' => '', 'response' => ['error' => 'Empty message after URL extraction.']];
|
||||
}
|
||||
|
||||
$textChunks = $textOnly !== '' ? $this->splitIntoThread($textOnly) : [];
|
||||
|
||||
if ($textChunks === [] && $urls !== []) {
|
||||
$textChunks = [implode(' ', $urls)];
|
||||
$urls = [];
|
||||
}
|
||||
|
||||
$firstResult = $this->postTweet($textChunks[0], null, $consumerKey, $consumerSecret, $accessToken, $tokenSecret);
|
||||
|
||||
if (!$firstResult['success']) {
|
||||
return $firstResult;
|
||||
}
|
||||
|
||||
$rootId = $firstResult['platform_post_id'];
|
||||
$previousId = $rootId;
|
||||
|
||||
for ($i = 1, $count = \count($textChunks); $i < $count; $i++) {
|
||||
$result = $this->postTweet($textChunks[$i], $previousId, $consumerKey, $consumerSecret, $accessToken, $tokenSecret);
|
||||
|
||||
if (!$result['success']) {
|
||||
return [
|
||||
'success' => false,
|
||||
'platform_post_id' => $rootId,
|
||||
'response' => ['error' => 'Cost-optimized thread failed at tweet ' . ($i + 1), 'root_tweet' => $rootId],
|
||||
];
|
||||
}
|
||||
|
||||
$previousId = $result['platform_post_id'];
|
||||
}
|
||||
|
||||
if ($urls !== []) {
|
||||
$urlText = implode(' ', $urls);
|
||||
$result = $this->postTweet($urlText, $previousId, $consumerKey, $consumerSecret, $accessToken, $tokenSecret);
|
||||
|
||||
if (!$result['success']) {
|
||||
return [
|
||||
'success' => false,
|
||||
'platform_post_id' => $rootId,
|
||||
'response' => ['error' => 'Cost-optimized URL reply failed.', 'root_tweet' => $rootId],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return ['success' => true, 'platform_post_id' => $rootId, 'response' => $firstResult['response']];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an OAuth 1.0a Authorization header with HMAC-SHA1 signature.
|
||||
*/
|
||||
|
||||
@@ -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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</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.17</version>
|
||||
<version>01.08.18</version>
|
||||
<creationDate>2026-05-28</creationDate>
|
||||
<author>Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<extension type="package" method="upgrade">
|
||||
<name>MokoSuiteCross</name>
|
||||
<packagename>mokosuitecross</packagename>
|
||||
<version>01.08.17</version>
|
||||
<version>01.08.18</version>
|
||||
<creationDate>2026-05-28</creationDate>
|
||||
<author>Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
|
||||
Reference in New Issue
Block a user