generated from MokoConsulting/Template-Joomla
feat: initial scaffold — package manifest with dlid/updateservers + first helper
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
namespace Moko\Plugin\System\MokoSuiteSupport\Helper;
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\Database\DatabaseInterface;
|
||||
|
||||
/**
|
||||
* Conversation management — create, message, assign, close conversations across all channels.
|
||||
*/
|
||||
class ConversationHelper
|
||||
{
|
||||
/**
|
||||
* Create a new conversation.
|
||||
*/
|
||||
public static function create(string $channel, ?int $contactId = null, ?string $visitorName = null): object
|
||||
{
|
||||
$allowedChannels = ['website', 'facebook', 'instagram', 'whatsapp', 'email'];
|
||||
if (!in_array($channel, $allowedChannels, true)) {
|
||||
$channel = 'website';
|
||||
}
|
||||
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
|
||||
$conversation = (object) [
|
||||
'channel' => $channel,
|
||||
'contact_id' => $contactId,
|
||||
'visitor_name' => $visitorName,
|
||||
'status' => 'open',
|
||||
'started_at' => Factory::getDate()->toSql(),
|
||||
];
|
||||
|
||||
$db->insertObject('#__mokosuitesupport_conversations', $conversation, 'id');
|
||||
|
||||
return (object) ['success' => true, 'conversation_id' => (int) $conversation->id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message in a conversation.
|
||||
*/
|
||||
public static function sendMessage(int $conversationId, string $body, string $senderType = 'visitor', ?int $senderId = null): object
|
||||
{
|
||||
$allowedSenders = ['visitor', 'agent', 'system'];
|
||||
if (!in_array($senderType, $allowedSenders, true)) {
|
||||
$senderType = 'visitor';
|
||||
}
|
||||
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
$filter = \Joomla\Filter\InputFilter::getInstance();
|
||||
|
||||
$message = (object) [
|
||||
'conversation_id' => $conversationId,
|
||||
'sender_type' => $senderType,
|
||||
'sender_id' => $senderId,
|
||||
'body' => $filter->clean($body, 'STRING'),
|
||||
'sent_at' => Factory::getDate()->toSql(),
|
||||
];
|
||||
|
||||
$db->insertObject('#__mokosuitesupport_messages', $message, 'id');
|
||||
|
||||
// Update conversation last_message_at
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->update('#__mokosuitesupport_conversations')
|
||||
->set('last_message_at = ' . $db->quote(Factory::getDate()->toSql()))
|
||||
->where('id = ' . (int) $conversationId));
|
||||
$db->execute();
|
||||
|
||||
return (object) ['success' => true, 'message_id' => (int) $message->id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unified inbox — all open conversations across channels.
|
||||
*/
|
||||
public static function getInbox(array $filters = [], int $limit = 50): array
|
||||
{
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->select('c.id, c.channel, c.status, c.visitor_name, c.started_at, c.last_message_at')
|
||||
->select('cd.name AS contact_name, cd.email_to')
|
||||
->select('ag.display_name AS agent_name')
|
||||
->select('(SELECT COUNT(*) FROM #__mokosuitesupport_messages m WHERE m.conversation_id = c.id) AS message_count')
|
||||
->select('(SELECT body FROM #__mokosuitesupport_messages m2 WHERE m2.conversation_id = c.id ORDER BY m2.sent_at DESC LIMIT 1) AS last_message')
|
||||
->from($db->quoteName('#__mokosuitesupport_conversations', 'c'))
|
||||
->join('LEFT', $db->quoteName('#__contact_details', 'cd') . ' ON cd.id = c.contact_id')
|
||||
->join('LEFT', $db->quoteName('#__mokosuitesupport_agents', 'ag') . ' ON ag.id = c.agent_id')
|
||||
->order('c.last_message_at DESC');
|
||||
|
||||
if (!empty($filters['status'])) {
|
||||
$query->where($db->quoteName('c.status') . ' = ' . $db->quote($filters['status']));
|
||||
} else {
|
||||
$query->where($db->quoteName('c.status') . ' IN (' . $db->quote('open') . ',' . $db->quote('assigned') . ')');
|
||||
}
|
||||
|
||||
if (!empty($filters['channel'])) {
|
||||
$query->where($db->quoteName('c.channel') . ' = ' . $db->quote($filters['channel']));
|
||||
}
|
||||
|
||||
if (!empty($filters['agent_id'])) {
|
||||
$query->where('c.agent_id = ' . (int) $filters['agent_id']);
|
||||
}
|
||||
|
||||
$db->setQuery($query, 0, min(max(1, $limit), 200));
|
||||
|
||||
return $db->loadObjectList() ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a conversation to an agent.
|
||||
*/
|
||||
public static function assign(int $conversationId, int $agentId): bool
|
||||
{
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
|
||||
$db->setQuery($db->getQuery(true)
|
||||
->update('#__mokosuitesupport_conversations')
|
||||
->set('agent_id = ' . (int) $agentId)
|
||||
->set($db->quoteName('status') . ' = ' . $db->quote('assigned'))
|
||||
->set('assigned_at = ' . $db->quote(Factory::getDate()->toSql()))
|
||||
->where('id = ' . (int) $conversationId));
|
||||
$db->execute();
|
||||
|
||||
return $db->getAffectedRows() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a conversation with optional CSAT rating.
|
||||
*/
|
||||
public static function close(int $conversationId, ?int $csatRating = null): bool
|
||||
{
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->update('#__mokosuitesupport_conversations')
|
||||
->set($db->quoteName('status') . ' = ' . $db->quote('closed'))
|
||||
->set('ended_at = ' . $db->quote(Factory::getDate()->toSql()))
|
||||
->where('id = ' . (int) $conversationId);
|
||||
|
||||
if ($csatRating !== null && $csatRating >= 1 && $csatRating <= 5) {
|
||||
$query->set('csat_rating = ' . (int) $csatRating);
|
||||
}
|
||||
|
||||
$db->setQuery($query);
|
||||
$db->execute();
|
||||
|
||||
return $db->getAffectedRows() > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<extension type="package" method="upgrade">
|
||||
<name>Package - MokoSuite Support</name>
|
||||
<packagename>mokosuitesupport</packagename>
|
||||
<version>01.00.00</version>
|
||||
<creationDate>2026-06-21</creationDate>
|
||||
<author>Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
|
||||
<license>GNU General Public License version 3 or later; see LICENSE</license>
|
||||
<description>MokoSuite Support - live chat, multi-channel customer support (website, Facebook, Instagram, WhatsApp, email). Interfaces with CRM contacts and ticket system.</description>
|
||||
<php_minimum>8.3</php_minimum>
|
||||
<dlid prefix="dlid=" suffix=""/>
|
||||
<blockChildUninstall>true</blockChildUninstall>
|
||||
<scriptfile>script.php</scriptfile>
|
||||
|
||||
<files folder="packages">
|
||||
<file type="plugin" id="plg_system_mokosuitesupport" group="system">plg_system_mokosuitesupport.zip</file>
|
||||
<file type="component" id="com_mokosuitesupport">com_mokosuitesupport.zip</file>
|
||||
</files>
|
||||
|
||||
<updateservers>
|
||||
<server type="extension" priority="1" name="Package - MokoSuite Support">https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteSupport/updates.xml</server>
|
||||
</updateservers>
|
||||
</extension>
|
||||
Reference in New Issue
Block a user