Browse Source

Handle <sticker/> tag from XEP-0449

feature/sticker-tag
Timothée Jaussoin 3 months ago
parent
commit
47bc63ecd6
  1. 10
      app/Message.php
  2. 2
      app/MessageOmemoHeader.php
  3. 22
      app/Widgets/Chat/Chat.php
  4. 22
      app/Widgets/Chat/chat.js
  5. 9
      app/Widgets/SendTo/SendTo.php
  6. 27
      app/Widgets/Stickers/Stickers.php
  7. 2
      app/Widgets/Stickers/_stickers_gifs_result.tpl
  8. 12
      app/Widgets/StoriesViewer/StoriesViewer.php
  9. 21
      database/migrations/20260501212107_add_sticker_to_messages_table.php
  10. 9
      src/Moxl/Stanza/Message.php
  11. 10
      src/Moxl/Xec/Action/Message/Publish.php

10
app/Message.php

@ -370,6 +370,14 @@ class Message extends Model
}
}
# XEP-0449: Stickers
if (
$stanza->sticker
&& (string)$stanza->sticker->attributes()->xmlns = 'urn:xmpp:stickers:0'
) {
$this->sticker = true;
}
# XEP-0444: Message Reactions
if (
isset($stanza->reactions)
@ -735,7 +743,7 @@ class Message extends Model
public function isEmpty(): bool
{
return (empty($this->body)
&& empty($this->sticker)
&& empty($this->stickerObject)
&& !$this->file
);
}

2
app/MessageOmemoHeader.php

@ -9,7 +9,7 @@ class MessageOmemoHeader
private $iv;
private $payload;
public function import($omemo)
public function import(\stdClass $omemo)
{
$this->sid = $omemo->sid;
$this->keys = $omemo->keys;

22
app/Widgets/Chat/Chat.php

@ -600,10 +600,10 @@ class Chat extends \Movim\Widget\Base
string $to,
string $message,
bool $muc = false,
$file = null,
?\stdClass $file = null,
?int $replyToMid = 0,
?bool $mucReceipts = false,
$omemo = null
?\stdClass $omemo = null
) {
$messageFile = null;
$messageOMEMOHeader = null;
@ -646,13 +646,14 @@ class Chat extends \Movim\Widget\Base
*/
public function sendMessage(
string $to,
string $message = '',
bool $muc = false,
?string $message = '',
?bool $muc = false,
?Message $replace = null,
?MessageFile $file = null,
?int $replyToMid = 0,
?bool $mucReceipts = false,
?MessageOMEMOHeader $messageOMEMOHeader = null
?MessageOMEMOHeader $messageOMEMOHeader = null,
?bool $isSticker = false,
) {
$tempId = null;
@ -723,6 +724,10 @@ class Chat extends \Movim\Widget\Base
}
}
if ($isSticker) {
$p->isSticker();
}
if ($file) {
$p->setFile($file);
$m->resolved = true;
@ -1487,8 +1492,7 @@ class Chat extends \Movim\Widget\Base
->request();
} else {
$stickerSize = $stickerImage->getGeometry();
$message->sticker = [
$message->stickerObject = [
'url' => Image::getOrCreate($stickerImage->getKey()), // Todo, don't reload
'width' => $stickerSize['width'],
'height' => $stickerSize['height']
@ -1504,7 +1508,7 @@ class Chat extends \Movim\Widget\Base
&& !isset($message->html)
&& $message->isClassic()
) {
$message->sticker = [
$message->stickerObject = [
'url' => $emoji->getLastSingleEmojiURL(),
'title' => ':' . $emoji->getLastSingleEmojiTitle() . ':',
'height' => 60,
@ -1555,7 +1559,7 @@ class Chat extends \Movim\Widget\Base
if (
$message->resolvedUrl && !$message->file
&& !$message->card && !$message->sticker
&& !$message->card && !$message->stickerObject
) {
$resolved = $message->resolvedUrl;
if ($resolved) {

22
app/Widgets/Chat/chat.js

@ -1084,7 +1084,7 @@ var Chat = {
&& msgStack.parentNode == refBubble
&& data.url === false
&& (data.file === undefined || data.file === null)
&& (data.sticker === undefined || data.sticker === null)
&& (data.stickerObject === undefined || data.stickerObject === null)
&& !refBubble.querySelector('div.bubble').classList.contains('sticker')
&& !refBubble.querySelector('div.bubble').classList.contains('file')
&& Chat.jingleTypes.indexOf(data.type) < 0
@ -1214,7 +1214,7 @@ var Chat = {
msg.appendChild(resourceSpan);
}
if (data.sticker != null && data.retracted == false) {
if (data.stickerObject != null && data.retracted == false) {
bubble.querySelector('div.bubble').classList.add('file');
p.appendChild(Chat.getStickerHtml(data));
@ -1241,7 +1241,7 @@ var Chat = {
msg.dataset.published = data.published;
if (omemo == false && data.sticker === undefined) {
if (omemo == false && data.stickerObject === undefined) {
Chat.addQuotesHtml(msg, data.body);
}
@ -1289,7 +1289,7 @@ var Chat = {
msg.parentElement.classList.remove('file');
}
if (data.sticker != null && data.retracted == false) {
if (data.stickerObject != null && data.retracted == false) {
msg.parentElement.classList.add('sticker', 'file');
} else {
msg.parentElement.classList.remove('sticker');
@ -1387,16 +1387,16 @@ var Chat = {
var img = document.createElement('img');
img.classList.add('sticker');
if (data.sticker.url) {
if (data.sticker.thumb) {
img.setAttribute('src', data.sticker.thumb);
if (data.stickerObject.url) {
if (data.stickerObject.thumb) {
img.setAttribute('src', data.stickerObject.thumb);
} else {
img.setAttribute('src', data.sticker.url);
img.setAttribute('src', data.stickerObject.url);
}
if (data.sticker.width) img.setAttribute('width', data.sticker.width);
if (data.sticker.height) {
img.setAttribute('height', data.sticker.height);
if (data.stickerObject.width) img.setAttribute('width', data.stickerObject.width);
if (data.stickerObject.height) {
img.setAttribute('height', data.stickerObject.height);
} else {
img.setAttribute('height', '170');
}

9
app/Widgets/SendTo/SendTo.php

@ -105,11 +105,10 @@ class SendTo extends Base
$file->url = $uri;
foreach ($contacts as $contact => $muc) {
$c = new Chat($this->me, sessionId: $this->sessionId);
$c->sendMessage(
$contact,
$message,
$muc,
(new Chat($this->me, sessionId: $this->sessionId))->sendMessage(
to: $contact,
message: $message,
muc: $muc,
file: $file
);
}

27
app/Widgets/Stickers/Stickers.php

@ -102,6 +102,7 @@ class Stickers extends \Movim\Widget\Base
$p->setTo($m->jidto)
->setContent($m->body)
->setHTML($dom->saveXML($dom->documentElement))
->isSticker()
->setId($m->id);
if ($muc) {
@ -195,7 +196,7 @@ class Stickers extends \Movim\Widget\Base
requestAsyncURL(
'https://tenor.googleapis.com/v2/search?q=' . $keyword .
'&media_filter=preview,tinywebm' .
'&media_filter=preview,tinymp4' .
'&key=' . $apiKey .
'&limit=' . $this->paginate .
'&pos=' . ($page * $this->paginate)
@ -208,10 +209,10 @@ class Stickers extends \Movim\Widget\Base
foreach ($results->results as $result) {
$gif = [
'id' => (string)$result->id,
'url' => (string)$result->media_formats->tinywebm->url,
'url' => (string)$result->media_formats->tinymp4->url,
'preview' => (string)$result->media_formats->preview->url,
'width' => (int)$result->media_formats->tinywebm->dims[0],
'height' => (int)$result->media_formats->tinywebm->dims[1],
'width' => (int)$result->media_formats->tinymp4->dims[0],
'height' => (int)$result->media_formats->tinymp4->dims[1],
];
$view->assign('gif', $gif);
@ -242,7 +243,7 @@ class Stickers extends \Movim\Widget\Base
requestAsyncURL(
'https://tenor.googleapis.com/v2/posts?ids=' . $gifId .
'&media_filter=preview,tinywebm' .
'&media_filter=preview,tinymp4' .
'&key=' . $apiKey
)->then(function (ResponseInterface $response) use ($to, $muc) {
$results = \json_decode($response->getBody());
@ -253,22 +254,20 @@ class Stickers extends \Movim\Widget\Base
$messageFile = new MessageFile;
$messageFile->name = (string)$result->url;
$messageFile->url = (string)$result->media_formats->tinywebm->url;
$messageFile->url = (string)$result->media_formats->tinymp4->url;
$messageFile->type = 'video/webm';
$messageFile->size = (int)$result->media_formats->tinywebm->size;
$messageFile->size = (int)$result->media_formats->tinymp4->size;
$messageFile->thumbnail_type = 'image/png';
$messageFile->thumbnail_url = (string)$result->media_formats->preview->url;
$messageFile->thumbnail_width = (int)$result->media_formats->preview->dims[0];
$messageFile->thumbnail_height = (int)$result->media_formats->preview->dims[1];
$chat = new Chat($this->me, sessionId: $this->sessionId);
$chat->sendMessage(
$to,
false,
$muc,
null,
$messageFile
(new Chat($this->me, sessionId: $this->sessionId))->sendMessage(
to: $to,
muc: $muc,
file: $messageFile,
isSticker: true
);
}
}, function (\Exception $e) {

2
app/Widgets/Stickers/_stickers_gifs_result.tpl

@ -7,5 +7,5 @@
width="{$gif.width}"
height="{$gif.height}"
src="{$gif.url}"
type="video/webm">
type="video/mp4">
</video>

12
app/Widgets/StoriesViewer/StoriesViewer.php

@ -70,9 +70,9 @@ class StoriesViewer extends Base
if ($post) {
$p = $this->xmpp(new PostDelete);
$p->setTo($post->server)
->setNode($post->node)
->setId($post->nodeid)
->request();
->setNode($post->node)
->setId($post->nodeid)
->request();
}
$this->rpc('StoriesViewer.close');
@ -87,7 +87,11 @@ class StoriesViewer extends Base
$file->type = 'xmpp/uri';
$file->url = $post->getRef();
(new Chat($this->me, sessionId: $this->sessionId))->sendMessage($post->server, $comment, file: $file);
(new Chat($this->me, sessionId: $this->sessionId))->sendMessage(
to: $post->server,
message: $comment,
file: $file
);
$this->toast($this->__('post.comment_published'));
}

21
database/migrations/20260501212107_add_sticker_to_messages_table.php

@ -0,0 +1,21 @@
<?php
use Movim\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddStickerToMessagesTable extends Migration
{
public function up()
{
$this->schema->table('messages', function (Blueprint $table) {
$table->boolean('sticker')->default(false);
});
}
public function down()
{
$this->schema->table('messages', function (Blueprint $table) {
$table->dropColumn('sticker');
});
}
}

9
src/Moxl/Stanza/Message.php

@ -42,7 +42,8 @@ class Message
$replyId = false,
$replyTo = false,
$replyQuotedBodyLength = 0,
?MessageOmemoHeader $messageOMEMO = null
?MessageOmemoHeader $messageOMEMO = null,
?bool $isSticker = false,
) {
$dom = Message::factory($to, $type, $messageId);
@ -258,6 +259,12 @@ class Message
$dom->documentElement->appendChild($origin);
}
if ($isSticker) {
$sticker = $dom->createElement('sticker');
$sticker->setAttribute('xmlns', 'urn:xmpp:stickers:0');
$dom->documentElement->appendChild($sticker);
}
// OMEMO
if ($messageOMEMO) {
$encryption = $dom->createElement('encryption');

10
src/Moxl/Xec/Action/Message/Publish.php

@ -20,6 +20,7 @@ class Publish extends Action
protected $_attachid = false;
protected $_originid = false;
protected $_threadid = false;
protected $_issticker = false;
// Reply
protected $_replyid = false;
@ -51,7 +52,8 @@ class Publish extends Action
replyId: $this->_replyid,
replyTo: $this->_replyto,
replyQuotedBodyLength: $this->_replyquotedbodylength,
messageOMEMO: $this->_messageOMEMO
messageOMEMO: $this->_messageOMEMO,
isSticker: $this->_issticker
));
}
@ -72,6 +74,12 @@ class Publish extends Action
return $this;
}
public function isSticker()
{
$this->_issticker = true;
return $this;
}
public function getMuc()
{
return $this->_muc;

Loading…
Cancel
Save