3 * @copyright Copyright (C) 2010-2023, the Friendica project
5 * @license GNU AGPL version 3 or any later version
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 namespace Friendica\Model\Post;
24 use Friendica\Content\Text\BBCode;
25 use Friendica\Core\Logger;
26 use Friendica\Core\Protocol;
27 use Friendica\Database\Database;
28 use Friendica\Database\DBA;
30 use Friendica\Model\Contact;
31 use Friendica\Model\Item;
32 use Friendica\Model\Post;
33 use Friendica\Model\Tag;
34 use Friendica\Model\Verb;
35 use Friendica\Protocol\Activity;
36 use Friendica\Protocol\Relay;
37 use Friendica\Util\DateTimeFormat;
44 * Store engagement data from an item array
49 public static function storeFromItem(array $item)
51 if (in_array($item['verb'], [Activity::FOLLOW, Activity::VIEW, Activity::READ])) {
52 Logger::debug('Technical activities are not stored', ['uri-id' => $item['uri-id'], 'parent-uri-id' => $item['parent-uri-id'], 'verb' => $item['verb']]);
56 $parent = Post::selectFirst(['uri-id', 'created', 'author-id', 'owner-id', 'uid', 'private', 'contact-contact-type', 'language', 'network',
57 'title', 'content-warning', 'body', 'author-contact-type', 'author-nick', 'author-addr', 'owner-contact-type', 'owner-nick', 'owner-addr'],
58 ['uri-id' => $item['parent-uri-id']]);
60 if ($parent['created'] < DateTimeFormat::utc('now - ' . DI::config()->get('channel', 'engagement_hours') . ' hour')) {
61 Logger::debug('Post is too old', ['uri-id' => $item['uri-id'], 'parent-uri-id' => $item['parent-uri-id'], 'created' => $parent['created']]);
65 $store = ($item['gravity'] != Item::GRAVITY_PARENT);
68 $store = Contact::hasFollowers($parent['owner-id']);
72 $tagList = Relay::getSubscribedTags();
73 foreach (array_column(Tag::getByURIId($item['parent-uri-id'], [Tag::HASHTAG]), 'name') as $tag) {
74 if (in_array($tag, $tagList)) {
81 $mediatype = self::getMediaType($item['parent-uri-id']);
84 $mediatype = !empty($mediatype);
88 'uri-id' => $item['parent-uri-id'],
89 'owner-id' => $parent['owner-id'],
90 'contact-type' => $parent['contact-contact-type'],
91 'media-type' => $mediatype,
92 'language' => $parent['language'],
93 'searchtext' => self::getSearchText($parent),
94 'created' => $parent['created'],
95 'restricted' => !in_array($item['network'], Protocol::FEDERATED) || ($parent['private'] != Item::PUBLIC),
96 'comments' => DBA::count('post', ['parent-uri-id' => $item['parent-uri-id'], 'gravity' => Item::GRAVITY_COMMENT]),
97 'activities' => DBA::count('post', [
98 "`parent-uri-id` = ? AND `gravity` = ? AND NOT `vid` IN (?, ?, ?)",
99 $item['parent-uri-id'], Item::GRAVITY_ACTIVITY,
100 Verb::getID(Activity::FOLLOW), Verb::getID(Activity::VIEW), Verb::getID(Activity::READ)
103 if (!$store && ($engagement['comments'] == 0) && ($engagement['activities'] == 0)) {
104 Logger::debug('No media, follower, subscribed tags, comments or activities. Engagement not stored', ['fields' => $engagement]);
107 $ret = DBA::insert('post-engagement', $engagement, Database::INSERT_UPDATE);
108 Logger::debug('Engagement stored', ['fields' => $engagement, 'ret' => $ret]);
111 private static function getSearchText(array $item): string
113 $body = '[nosmile]network:' . $item['network'];
115 switch ($item['private']) {
117 $body .= ' visibility:public';
120 $body .= ' visibility:unlisted';
123 $body .= ' visibility:private';
127 if ($item['author-contact-type'] == Contact::TYPE_COMMUNITY) {
128 $body .= ' group:' . $item['author-nick'] . ' group:' . $item['author-addr'];
129 } elseif (in_array($item['author-contact-type'], [Contact::TYPE_PERSON, Contact::TYPE_NEWS, Contact::TYPE_ORGANISATION])) {
130 $body .= ' from:' . $item['author-nick'] . ' from:' . $item['author-addr'];
133 if ($item['author-id'] != $item['owner-id']) {
134 if ($item['owner-contact-type'] == Contact::TYPE_COMMUNITY) {
135 $body .= ' group:' . $item['owner-nick'] . ' group:' . $item['owner-addr'];
136 } elseif (in_array($item['owner-contact-type'], [Contact::TYPE_PERSON, Contact::TYPE_NEWS, Contact::TYPE_ORGANISATION])) {
137 $body .= ' from:' . $item['owner-nick'] . ' from:' . $item['owner-addr'];
141 foreach (Tag::getByURIId($item['uri-id'], [Tag::MENTION, Tag::IMPLICIT_MENTION, Tag::EXCLUSIVE_MENTION, Tag::AUDIENCE]) as $tag) {
142 $contact = Contact::getByURL($tag['name'], false, ['nick', 'addr', 'contact-type']);
143 if (empty($contact)) {
147 if (($contact['contact-type'] == Contact::TYPE_COMMUNITY) && !strpos($body, 'group:' . $contact['addr'])) {
148 $body .= ' group:' . $contact['nick'] . ' group:' . $contact['addr'];
149 } elseif (in_array($contact['contact-type'], [Contact::TYPE_PERSON, Contact::TYPE_NEWS, Contact::TYPE_ORGANISATION])) {
150 $body .= ' to:' . $contact['nick'] . ' to:' . $contact['addr'];
154 foreach (Tag::getByURIId($item['uri-id'], [Tag::HASHTAG]) as $tag) {
155 $body .= ' tag:' . $tag['name'];
158 $body .= ' ' . $item['title'] . ' ' . $item['content-warning'] . ' ' . $item['body'];
160 $body = Post\Media::addAttachmentsToBody($item['uri-id'], $body);
161 $text = BBCode::toPlaintext($body, false);
165 $text = str_replace([' ', "\n", "\r"], ' ', $text);
166 } while ($oldtext != $text);
171 private static function getMediaType(int $uri_id): int
173 $media = Post\Media::getByURIId($uri_id);
175 foreach ($media as $entry) {
176 if ($entry['type'] == Post\Media::IMAGE) {
178 } elseif ($entry['type'] == Post\Media::VIDEO) {
180 } elseif ($entry['type'] == Post\Media::AUDIO) {
188 * Expire old engagement data
192 public static function expire()
194 DBA::delete('post-engagement', ["`created` < ?", DateTimeFormat::utc('now - ' . DI::config()->get('channel', 'engagement_hours') . ' hour')]);
195 Logger::notice('Cleared expired engagements', ['rows' => DBA::affectedRows()]);