Compare commits

...

32 commits

Author SHA1 Message Date
Tobias Diekershoff ef37aa60e3 Merge pull request 'Bluesky: Preparation for video posts' (#1554) from heluecht/friendica-addons:hls into develop
Reviewed-on: #1554
2024-09-17 07:02:47 +02:00
Michael 6f3ba10466 Bluesky: Preparation for video posts 2024-09-17 07:02:47 +02:00
Tobias Diekershoff 778b9e3f61 Merge pull request 'More and updated icons for the smiley pack' (#1555) from heluecht/friendica-addons:loma-patch into develop
Reviewed-on: #1555
2024-09-17 07:02:03 +02:00
loma-one 10521115c4 More and updated icons for the smiley pack 2024-09-16 21:20:11 +00:00
Tobias Diekershoff 956233ff1d Merge pull request 'Bluesky: Fix for the handling of invalid profiles' (#1553) from heluecht/friendica-addons:bluesky-fix into develop
Reviewed-on: #1553
2024-09-11 19:42:59 +02:00
Michael 14e1c96775 Bluesky: Fix for the handling of invalid profiles 2024-09-10 10:26:05 +00:00
Tobias Diekershoff 7a7dbb579d Merge pull request 'invidious updated' (#1537) from loma-one/friendica-addons:develop into develop
Reviewed-on: #1537
2024-09-08 08:50:39 +02:00
loma-one 712edf4236 invidious/invidious.php aktualisiert
Further addresses have been added, which are now redirected.
2024-09-08 08:50:39 +02:00
Tobias Diekershoff 5c0cddfc1d Merge pull request 'unicode_smilies updated' (#1536) from loma-one/friendica-addons:loma-one-patch-1 into develop
Reviewed-on: #1536
2024-09-08 08:50:12 +02:00
loma-one 3dc77b2102 unicode_smilies/unicode_smilies.php aktualisiert
Addition of the unicode character ‘asterism’ & ‘outlines white star’
2024-09-07 21:04:43 +02:00
Tobias Diekershoff 6c43a14198 Merge pull request '"fetchFull" is replaced by "get"' (#1535) from heluecht/friendica-addons:fetchfull into develop
Reviewed-on: #1535
2024-09-06 07:22:58 +02:00
Michael 0dfb345f85 "fetchFull" is replaced by "get" 2024-09-06 07:22:58 +02:00
Tobias Diekershoff ab837dfec5 Merge pull request 'Bluesky: probing for bluesky handles' (#1534) from heluecht/friendica-addons:bluesky-handle into develop
Reviewed-on: #1534
2024-09-06 07:20:55 +02:00
Michael 2f9076bffd Bluesky: probing for bluesky handles 2024-09-04 04:02:30 +00:00
Tobias Diekershoff 454e9834bf Merge pull request 'Bluesky: Improve DID detection for custom PDS' (#1533) from heluecht/friendica-addons:bluesky-pds into develop
Reviewed-on: #1533
2024-09-02 06:32:54 +02:00
Michael 50930c301d Bluesky: Improve DID detection for custom PDS 2024-09-02 06:32:54 +02:00
Philipp Holzer 3457ab2f3f Merge pull request 'Add safe.directory config' (#1532) from nupplaPhil/friendica-addons:bug/ci into develop
Reviewed-on: #1532
2024-08-23 20:24:16 +02:00
Philipp Holzer 276c27678f
[CI] Add safe.directory config 2024-08-20 18:07:51 +02:00
Tobias Diekershoff cd95ca1a0a Merge branch 'stable' into develop 2024-08-17 16:55:10 +02:00
Tobias Diekershoff 4414471100 Merge pull request 'Ratioed: add help text' (#1528) from mexon/friendica-addons:mat/ratioed-help into develop
Reviewed-on: #1528
2024-08-09 13:58:48 +02:00
Matthew Exon 46a55f13f7 Ratioed: add help text 2024-08-09 13:58:48 +02:00
Tobias Diekershoff 0c04b086cb Merge pull request 'Remove old version conversion code' (#1526) from mexon/friendica-addons:mat/remove-conversion into develop
Reviewed-on: #1526
2024-07-29 19:17:39 +02:00
Matthew Exon 589cf712cc Remove old version conversion code 2024-07-20 13:00:07 +02:00
heluecht ce53e48cb2 Merge pull request 'More comprehensible check for root user contact' (#1525) from mexon/friendica-addons:mat/mailstream-clarify-log into develop
Reviewed-on: #1525
2024-07-20 11:57:32 +02:00
Matthew Exon f3db763c59 More comprehensible check for root user contact 2024-07-14 18:50:56 +02:00
heluecht 4e5998c73d Merge pull request 'Mailstream: streamline log lines' (#1522) from mexon/friendica-addons:mat/mailstream-log into develop
Reviewed-on: #1522
2024-07-14 18:10:10 +02:00
Matthew Exon 5f27f72b0d Streamline log lines 2024-07-01 19:12:10 +02:00
Hypolite Petovan d3dcd5428c Merge pull request 'mat/ratioed-plugin-2' (#1519) from mexon/friendica-addons:mat/ratioed-plugin-2 into develop
Reviewed-on: #1519
Reviewed-by: Hypolite Petovan <hypolite@mrpetovan.com>
2024-06-23 21:25:47 +02:00
Matthew Exon 18e512cc8b Ratioed: move panel class into separate file 2024-06-23 15:43:05 +02:00
Matthew Exon 7d5446a778 Ratioed: remove unnecessary uninstall function 2024-06-23 15:15:06 +02:00
Hypolite Petovan 38ea90104d Merge pull request 'New addon providing additional statistics for moderation' (#1518) from mexon/friendica-addons:mat/ratioed-plugin into develop
Reviewed-on: #1518
2024-06-23 14:13:34 +02:00
Matthew Exon 08b46e5536 New addon providing additional statistics for moderation 2024-06-22 18:56:32 +02:00
65 changed files with 906 additions and 200 deletions

View file

@ -4,6 +4,9 @@ pipeline:
clone_friendica_base: clone_friendica_base:
image: alpine/git image: alpine/git
commands: commands:
- git config --global user.email "no-reply@friendi.ca"
- git config --global user.name "Friendica"
- git config --global --add safe.directory $CI_WORKSPACE
- git clone https://github.com/friendica/friendica.git . - git clone https://github.com/friendica/friendica.git .
- git checkout $CI_COMMIT_BRANCH - git checkout $CI_COMMIT_BRANCH
when: when:

View file

@ -9,6 +9,9 @@ pipeline:
clone_friendica_base: clone_friendica_base:
image: alpine/git image: alpine/git
commands: commands:
- git config --global user.email "no-reply@friendi.ca"
- git config --global user.name "Friendica"
- git config --global --add safe.directory $CI_WORKSPACE
- git clone https://github.com/friendica/friendica.git . - git clone https://github.com/friendica/friendica.git .
- git checkout $CI_COMMIT_BRANCH - git checkout $CI_COMMIT_BRANCH
when: when:

View file

@ -4,6 +4,9 @@ pipeline:
clone_friendica_base: clone_friendica_base:
image: alpine/git image: alpine/git
commands: commands:
- git config --global user.email "no-reply@friendi.ca"
- git config --global user.name "Friendica"
- git config --global --add safe.directory $CI_WORKSPACE
- git clone https://github.com/friendica/friendica.git . - git clone https://github.com/friendica/friendica.git .
- git checkout $CI_COMMIT_BRANCH - git checkout $CI_COMMIT_BRANCH
when: when:

View file

@ -21,6 +21,9 @@ pipeline:
clone_friendica_base: clone_friendica_base:
image: alpine/git image: alpine/git
commands: commands:
- git config --global user.email "no-reply@friendi.ca"
- git config --global user.name "Friendica"
- git config --global --add safe.directory $CI_WORKSPACE
- git clone https://github.com/friendica/friendica.git . - git clone https://github.com/friendica/friendica.git .
- git checkout $CI_COMMIT_BRANCH - git checkout $CI_COMMIT_BRANCH
clone_friendica_addon: clone_friendica_addon:

View file

@ -9,6 +9,9 @@ pipeline:
clone_friendica_base: clone_friendica_base:
image: alpine/git image: alpine/git
commands: commands:
- git config --global user.email "no-reply@friendi.ca"
- git config --global user.name "Friendica"
- git config --global --add safe.directory $CI_WORKSPACE
- git clone https://github.com/friendica/friendica.git . - git clone https://github.com/friendica/friendica.git .
- git checkout $CI_COMMIT_BRANCH - git checkout $CI_COMMIT_BRANCH
when: when:

View file

@ -45,10 +45,12 @@ use Friendica\Model\Tag;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPClient\Client\HttpClientOptions; use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Network\HTTPClient\Client\HttpClientRequest;
use Friendica\Object\Image; use Friendica\Object\Image;
use Friendica\Protocol\Activity; use Friendica\Protocol\Activity;
use Friendica\Protocol\Relay; use Friendica\Protocol\Relay;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
use Friendica\Util\Strings; use Friendica\Util\Strings;
const BLUESKY_DEFAULT_POLL_INTERVAL = 10; // given in minutes const BLUESKY_DEFAULT_POLL_INTERVAL = 10; // given in minutes
@ -129,8 +131,13 @@ function bluesky_probe_detect(array &$hookData)
if (parse_url($hookData['uri'], PHP_URL_SCHEME) == 'did') { if (parse_url($hookData['uri'], PHP_URL_SCHEME) == 'did') {
$did = $hookData['uri']; $did = $hookData['uri'];
} elseif (preg_match('#^' . BLUESKY_WEB . '/profile/(.+)#', $hookData['uri'], $matches)) { } elseif (parse_url($hookData['uri'], PHP_URL_PATH) == $hookData['uri'] && strpos($hookData['uri'], '@') === false) {
$did = bluesky_get_did($matches[1]); $did = bluesky_get_did($hookData['uri'], $pconfig['uid']);
if (empty($did)) {
return;
}
} elseif (Network::isValidHttpUrl($hookData['uri'])) {
$did = bluesky_get_did_by_profile($hookData['uri']);
if (empty($did)) { if (empty($did)) {
return; return;
} }
@ -148,19 +155,14 @@ function bluesky_probe_detect(array &$hookData)
return; return;
} }
$hookData['result'] = bluesky_get_contact_fields($data, 0, $pconfig['uid'], false); $hookData['result'] = bluesky_get_contact_fields($data, 0, $pconfig['uid'], true);
$hookData['result']['baseurl'] = bluesky_get_pds($did);
// Preparing probe data. This differs slightly from the contact array // Preparing probe data. This differs slightly from the contact array
$hookData['result']['about'] = HTML::toBBCode($data->description ?? '');
$hookData['result']['photo'] = $data->avatar ?? ''; $hookData['result']['photo'] = $data->avatar ?? '';
$hookData['result']['header'] = $data->banner ?? '';
$hookData['result']['batch'] = ''; $hookData['result']['batch'] = '';
$hookData['result']['notify'] = ''; $hookData['result']['notify'] = '';
$hookData['result']['poll'] = ''; $hookData['result']['poll'] = '';
$hookData['result']['poco'] = ''; $hookData['result']['poco'] = '';
$hookData['result']['pubkey'] = '';
$hookData['result']['priority'] = 0; $hookData['result']['priority'] = 0;
$hookData['result']['guid'] = ''; $hookData['result']['guid'] = '';
} }
@ -177,21 +179,21 @@ function bluesky_item_by_link(array &$hookData)
return; return;
} }
if (!preg_match('#^' . BLUESKY_WEB . '/profile/(.+)/post/(.+)#', $hookData['uri'], $matches)) { $did = bluesky_get_did_by_profile($hookData['uri']);
return;
}
$did = bluesky_get_did($matches[1]);
if (empty($did)) { if (empty($did)) {
return; return;
} }
Logger::debug('Found bluesky post', ['url' => $hookData['uri'], 'handle' => $matches[1], 'did' => $did, 'cid' => $matches[2]]); if (!preg_match('#/profile/.+/post/(.+)#', $hookData['uri'], $matches)) {
return;
}
$uri = 'at://' . $did . '/app.bsky.feed.post/' . $matches[2]; Logger::debug('Found bluesky post', ['url' => $hookData['uri'], 'did' => $did, 'cid' => $matches[1]]);
$uri = 'at://' . $did . '/app.bsky.feed.post/' . $matches[1];
$uri = bluesky_fetch_missing_post($uri, $hookData['uid'], $hookData['uid'], Item::PR_FETCHED, 0, 0, 0); $uri = bluesky_fetch_missing_post($uri, $hookData['uid'], $hookData['uid'], Item::PR_FETCHED, 0, 0, 0);
Logger::debug('Got post', ['profile' => $matches[1], 'cid' => $matches[2], 'result' => $uri]); Logger::debug('Got post', ['did' => $did, 'cid' => $matches[1], 'result' => $uri]);
if (!empty($uri)) { if (!empty($uri)) {
$item = Post::selectFirst(['id'], ['uri' => $uri, 'uid' => $hookData['uid']]); $item = Post::selectFirst(['id'], ['uri' => $uri, 'uid' => $hookData['uid']]);
if (!empty($item['id'])) { if (!empty($item['id'])) {
@ -1408,6 +1410,19 @@ function bluesky_add_media(stdClass $embed, array $item, int $fetch_uid, int $le
} }
break; break;
case 'app.bsky.embed.video#view':
$media = [
'uri-id' => $item['uri-id'],
'type' => Post\Media::HLS,
'url' => $embed->playlist,
'preview' => $embed->thumbnail,
'description' => $embed->alt ?? '',
'height' => $embed->aspectRatio->height,
'width' => $embed->aspectRatio->width,
];
Post\Media::insert($media);
break;
case 'app.bsky.embed.external#view': case 'app.bsky.embed.external#view':
$media = [ $media = [
'uri-id' => $item['uri-id'], 'uri-id' => $item['uri-id'],
@ -1678,10 +1693,14 @@ function bluesky_get_contact_fields(stdClass $author, int $uid, int $fetch_uid,
return $fields; return $fields;
} }
$fields['baseurl'] = bluesky_get_pds($author->did); $data = bluesky_get(BLUESKY_DIRECTORY . '/' . $author->did);
if (!empty($fields['baseurl'])) { if (!empty($data)) {
GServer::check($fields['baseurl'], Protocol::BLUESKY); $fields['baseurl'] = bluesky_get_pds('', $data);
$fields['gsid'] = GServer::getID($fields['baseurl'], true); if (!empty($fields['baseurl'])) {
GServer::check($fields['baseurl'], Protocol::BLUESKY);
$fields['gsid'] = GServer::getID($fields['baseurl'], true);
}
$fields['pubkey'] = bluesky_get_public_key('', $data);
} }
$data = bluesky_xrpc_get($fetch_uid, 'app.bsky.actor.getProfile', ['actor' => $author->did]); $data = bluesky_xrpc_get($fetch_uid, 'app.bsky.actor.getProfile', ['actor' => $author->did]);
@ -1748,6 +1767,49 @@ function bluesky_get_preferences(int $uid): ?stdClass
return $data; return $data;
} }
function bluesky_get_did_by_profile(string $url): string
{
try {
$curlResult = DI::httpClient()->get($url, HttpClientAccept::HTML, [HttpClientOptions::REQUEST => HttpClientRequest::CONTACTINFO]);
} catch (\Throwable $th) {
return '';
}
if (!$curlResult->isSuccess()) {
return '';
}
$profile = $curlResult->getBodyString();
if (empty($profile)) {
return '';
}
$doc = new DOMDocument();
try {
@$doc->loadHTML($profile);
} catch (\Throwable $th) {
return '';
}
$xpath = new DOMXPath($doc);
$list = $xpath->query('//p[@id]');
foreach ($list as $node) {
foreach ($node->attributes as $attribute) {
if ($attribute->name == 'id') {
$ids[$attribute->value] = $node->textContent;
}
}
}
if (empty($ids['bsky_handle']) || empty($ids['bsky_did'])) {
return '';
}
if (!bluesky_valid_did($ids['bsky_did'], $ids['bsky_handle'])) {
Logger::notice('Invalid DID', ['handle' => $ids['bsky_handle'], 'did' => $ids['bsky_did']]);
return '';
}
return $ids['bsky_did'];
}
function bluesky_get_did_by_wellknown(string $handle): string function bluesky_get_did_by_wellknown(string $handle): string
{ {
$curlResult = DI::httpClient()->get('http://' . $handle . '/.well-known/atproto-did'); $curlResult = DI::httpClient()->get('http://' . $handle . '/.well-known/atproto-did');
@ -1757,7 +1819,6 @@ function bluesky_get_did_by_wellknown(string $handle): string
Logger::notice('Invalid DID', ['handle' => $handle, 'did' => $did]); Logger::notice('Invalid DID', ['handle' => $handle, 'did' => $did]);
return ''; return '';
} }
Logger::debug('Got DID by wellknown', ['handle' => $handle, 'did' => $did]);
return $did; return $did;
} }
return ''; return '';
@ -1776,14 +1837,13 @@ function bluesky_get_did_by_dns(string $handle): string
Logger::notice('Invalid DID', ['handle' => $handle, 'did' => $did]); Logger::notice('Invalid DID', ['handle' => $handle, 'did' => $did]);
return ''; return '';
} }
Logger::debug('Got DID by DNS', ['handle' => $handle, 'did' => $did]);
return $did; return $did;
} }
} }
return ''; return '';
} }
function bluesky_get_did(string $handle): string function bluesky_get_did(string $handle, int $uid): string
{ {
if ($handle == '') { if ($handle == '') {
return ''; return '';
@ -1793,23 +1853,39 @@ function bluesky_get_did(string $handle): string
$handle .= '.' . BLUESKY_HOSTNAME; $handle .= '.' . BLUESKY_HOSTNAME;
} }
// Deactivated at the moment, since it isn't reliable by now // At first we use the user PDS. That should cover most cases.
//$did = bluesky_get_did_by_dns($handle); $pds = DI::pConfig()->get($uid, 'bluesky', 'pds');
//if ($did != '') { if (!empty($pds)) {
// return $did; $data = bluesky_get($pds . '/xrpc/com.atproto.identity.resolveHandle?handle=' . urlencode($handle));
//} if (!empty($data) && !empty($data->did)) {
Logger::debug('Got DID by user PDS call', ['handle' => $handle, 'did' => $data->did]);
//$did = bluesky_get_did_by_wellknown($handle); return $data->did;
//if ($did != '') { }
// return $did;
//}
$data = bluesky_get(BLUESKY_PDS . '/xrpc/com.atproto.identity.resolveHandle?handle=' . urlencode($handle));
if (empty($data) || empty($data->did)) {
return '';
} }
Logger::debug('Got DID by PDS call', ['handle' => $handle, 'did' => $data->did]);
return $data->did; // Then we query the DNS, which is used for third party handles (DNS should be faster than wellknown)
$did = bluesky_get_did_by_dns($handle);
if ($did != '') {
Logger::debug('Got DID by DNS', ['handle' => $handle, 'did' => $did]);
return $did;
}
// Then we query wellknown, which should mostly cover the rest.
$did = bluesky_get_did_by_wellknown($handle);
if ($did != '') {
Logger::debug('Got DID by wellknown', ['handle' => $handle, 'did' => $did]);
return $did;
}
// And finally we use the default PDS from Bluesky.
$data = bluesky_get(BLUESKY_PDS . '/xrpc/com.atproto.identity.resolveHandle?handle=' . urlencode($handle));
if (!empty($data) && !empty($data->did)) {
Logger::debug('Got DID by system PDS call', ['handle' => $handle, 'did' => $data->did]);
return $data->did;
}
Logger::notice('No DID detected', ['handle' => $handle]);
return '';
} }
function bluesky_get_user_did(int $uid, bool $refresh = false): ?string function bluesky_get_user_did(int $uid, bool $refresh = false): ?string
@ -1826,7 +1902,7 @@ function bluesky_get_user_did(int $uid, bool $refresh = false): ?string
return null; return null;
} }
$did = bluesky_get_did($handle); $did = bluesky_get_did($handle, $uid);
if (empty($did)) { if (empty($did)) {
return null; return null;
} }
@ -1857,9 +1933,11 @@ function bluesky_get_user_pds(int $uid): ?string
return $pds; return $pds;
} }
function bluesky_get_pds(string $did): ?string function bluesky_get_pds(string $did, stdClass $data = null): ?string
{ {
$data = bluesky_get(BLUESKY_DIRECTORY . '/' . $did); if (empty($data)) {
$data = bluesky_get(BLUESKY_DIRECTORY . '/' . $did);
}
if (empty($data) || empty($data->service)) { if (empty($data) || empty($data->service)) {
return null; return null;
} }
@ -1873,6 +1951,22 @@ function bluesky_get_pds(string $did): ?string
return null; return null;
} }
function bluesky_get_public_key(string $did, stdClass $data = null): ?string
{
if (empty($data)) {
$data = bluesky_get(BLUESKY_DIRECTORY . '/' . $did);
}
if (empty($data) || empty($data->verificationMethod)) {
return null;
}
foreach ($data->verificationMethod as $method) {
if (!empty($method->publicKeyMultibase)) {
return $method->publicKeyMultibase;
}
}
return null;
}
function bluesky_valid_did(string $did, string $handle): bool function bluesky_valid_did(string $did, string $handle): bool
{ {
$data = bluesky_get(BLUESKY_DIRECTORY . '/' . $did); $data = bluesky_get(BLUESKY_DIRECTORY . '/' . $did);

View file

@ -96,6 +96,8 @@ function invidious_render(array &$b)
$b['html'] = preg_replace("~https?://(?:www\.)?youtube\.com/watch\?v=(.*?)~ism", $server . '/watch?v=$1', $b['html']); $b['html'] = preg_replace("~https?://(?:www\.)?youtube\.com/watch\?v=(.*?)~ism", $server . '/watch?v=$1', $b['html']);
$b['html'] = preg_replace("~https?://(?:www\.)?youtube\.com/embed/(.*?)~ism", $server . '/embed/$1', $b['html']); $b['html'] = preg_replace("~https?://(?:www\.)?youtube\.com/embed/(.*?)~ism", $server . '/embed/$1', $b['html']);
$b['html'] = preg_replace("~https?://(?:www\.)?youtube\.com/shorts/(.*?)~ism", $server . '/shorts/$1', $b['html']); $b['html'] = preg_replace("~https?://(?:www\.)?youtube\.com/shorts/(.*?)~ism", $server . '/shorts/$1', $b['html']);
$b['html'] = preg_replace ("/https?:\/\/music.youtube.com\/(.*?)/ism", $server . '/watch?v=$1', $b['html']);
$b['html'] = preg_replace ("/https?:\/\/m.youtube.com\/(.*?)/ism", $server . '/watch?v=$1', $b['html']);
$b['html'] = preg_replace("/https?:\/\/youtu.be\/(.*?)/ism", $server . '/watch?v=$1', $b['html']); $b['html'] = preg_replace("/https?:\/\/youtu.be\/(.*?)/ism", $server . '/watch?v=$1', $b['html']);
if ($original != $b['html']) { if ($original != $b['html']) {

View file

@ -6,7 +6,6 @@
* Author: Matthew Exon <http://mat.exon.name> * Author: Matthew Exon <http://mat.exon.name>
*/ */
use Friendica\App;
use Friendica\Content\Text\BBCode; use Friendica\Content\Text\BBCode;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\Logger; use Friendica\Core\Logger;
@ -16,12 +15,11 @@ use Friendica\Core\Worker;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\Item;
use Friendica\Model\Post; use Friendica\Model\Post;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Network\HTTPClient\Client\HttpClientAccept; use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Protocol\Activity; use Friendica\Protocol\Activity;
use Friendica\Util\DateTimeFormat;
/** /**
* Sets up the addon hooks and the database table * Sets up the addon hooks and the database table
@ -34,26 +32,7 @@ function mailstream_install()
Hook::register('post_remote_end', 'addon/mailstream/mailstream.php', 'mailstream_post_hook'); Hook::register('post_remote_end', 'addon/mailstream/mailstream.php', 'mailstream_post_hook');
Hook::register('mailstream_send_hook', 'addon/mailstream/mailstream.php', 'mailstream_send_hook'); Hook::register('mailstream_send_hook', 'addon/mailstream/mailstream.php', 'mailstream_send_hook');
Logger::info("mailstream: installed"); Logger::info("installed mailstream");
}
/**
* Enforces that mailstream_install has set up the current version
*/
function mailstream_check_version()
{
if (!is_null(DI::config()->get('mailstream', 'dbversion'))) {
DI::config()->delete('mailstream', 'dbversion');
Logger::info("mailstream_check_version: old version detected, reinstalling");
mailstream_install();
Hook::loadHooks();
Hook::add(
'mailstream_convert_table_entries',
'addon/mailstream/mailstream.php',
'mailstream_convert_table_entries'
);
Hook::fork(Worker::PRIORITY_LOW, 'mailstream_convert_table_entries');
}
} }
/** /**
@ -72,10 +51,12 @@ function mailstream_addon_admin(string &$o)
{ {
$frommail = DI::config()->get('mailstream', 'frommail'); $frommail = DI::config()->get('mailstream', 'frommail');
$template = Renderer::getMarkupTemplate('admin.tpl', 'addon/mailstream/'); $template = Renderer::getMarkupTemplate('admin.tpl', 'addon/mailstream/');
$config = ['frommail', $config = [
'frommail',
DI::l10n()->t('From Address'), DI::l10n()->t('From Address'),
$frommail, $frommail,
DI::l10n()->t('Email address that stream items will appear to be from.')]; DI::l10n()->t('Email address that stream items will appear to be from.')
];
$o .= Renderer::replaceMacros($template, [ $o .= Renderer::replaceMacros($template, [
'$frommail' => $config, '$frommail' => $config,
'$submit' => DI::l10n()->t('Save Settings') '$submit' => DI::l10n()->t('Save Settings')
@ -105,7 +86,7 @@ function mailstream_generate_id(string $uri): string
$host = DI::baseUrl()->getHost(); $host = DI::baseUrl()->getHost();
$resource = hash('md5', $uri); $resource = hash('md5', $uri);
$message_id = "<" . $resource . "@" . $host . ">"; $message_id = "<" . $resource . "@" . $host . ">";
Logger::debug('mailstream: Generated message ID ' . $message_id . ' for URI ' . $uri); Logger::debug('generated message ID', ['id' => $message_id, 'uri' => $uri]);
return $message_id; return $message_id;
} }
@ -114,20 +95,20 @@ function mailstream_send_hook(array $data)
$criteria = array('uid' => $data['uid'], 'contact-id' => $data['contact-id'], 'uri' => $data['uri']); $criteria = array('uid' => $data['uid'], 'contact-id' => $data['contact-id'], 'uri' => $data['uri']);
$item = Post::selectFirst([], $criteria); $item = Post::selectFirst([], $criteria);
if (empty($item)) { if (empty($item)) {
Logger::error('mailstream_send_hook could not find item'); Logger::error('could not find item');
return; return;
} }
$user = User::getById($item['uid']); $user = User::getById($item['uid']);
if (empty($user)) { if (empty($user)) {
Logger::error('mailstream_send_hook could not fund user', ['uid' => $item['uid']]); Logger::error('could not find user', ['uid' => $item['uid']]);
return; return;
} }
if (!mailstream_send($data['message_id'], $item, $user)) { if (!mailstream_send($data['message_id'], $item, $user)) {
Logger::debug('mailstream_send_hook send failed, will retry', $data); Logger::debug('send failed, will retry', $data);
if (!Worker::defer()) { if (!Worker::defer()) {
Logger::error('mailstream_send_hook failed and could not defer', $data); Logger::error('failed and could not defer', $data);
} }
} }
} }
@ -142,35 +123,33 @@ function mailstream_send_hook(array $data)
*/ */
function mailstream_post_hook(array &$item) function mailstream_post_hook(array &$item)
{ {
mailstream_check_version(); if ($item['uid'] === 0) {
Logger::debug('mailstream: root user, skipping item ' . $item['id']);
return;
}
if (!DI::pConfig()->get($item['uid'], 'mailstream', 'enabled')) { if (!DI::pConfig()->get($item['uid'], 'mailstream', 'enabled')) {
Logger::debug('mailstream: not enabled.', ['item' => $item['id'], ' uid ' => $item['uid']]); Logger::debug('mailstream: not enabled.', ['item' => $item['id'], ' uid ' => $item['uid']]);
return; return;
} }
if (!$item['uid']) {
Logger::debug('mailstream: no uid for item ' . $item['id']);
return;
}
if (!$item['contact-id']) { if (!$item['contact-id']) {
Logger::debug('mailstream: no contact-id for item ' . $item['id']); Logger::debug('no contact-id', ['item' => $item['id']]);
return; return;
} }
if (!$item['uri']) { if (!$item['uri']) {
Logger::debug('mailstream: no uri for item ' . $item['id']); Logger::debug('no uri', ['item' => $item['id']]);
return; return;
} }
if ($item['verb'] == Activity::ANNOUNCE) { if ($item['verb'] == Activity::ANNOUNCE) {
Logger::debug('mailstream: announce item ', ['item' => $item['id']]); Logger::debug('ignoring announce', ['item' => $item['id']]);
return; return;
} }
if (DI::pConfig()->get($item['uid'], 'mailstream', 'nolikes')) { if (DI::pConfig()->get($item['uid'], 'mailstream', 'nolikes')) {
if ($item['verb'] == Activity::LIKE) { if ($item['verb'] == Activity::LIKE) {
Logger::debug('mailstream: like item ' . $item['id']); Logger::debug('ignoring like', ['item' => $item['id']]);
return; return;
} }
if ($item['verb'] == Activity::DISLIKE) { if ($item['verb'] == Activity::DISLIKE) {
Logger::debug('mailstream: dislike item ' . $item['id']); Logger::debug('ignoring dislike', ['item' => $item['id']]);
return; return;
} }
} }
@ -219,14 +198,17 @@ function mailstream_do_images(array &$item, array &$attachments)
$cookiejar = tempnam(System::getTempPath(), 'cookiejar-mailstream-'); $cookiejar = tempnam(System::getTempPath(), 'cookiejar-mailstream-');
try { try {
$curlResult = DI::httpClient()->fetchFull($url, HttpClientAccept::DEFAULT, 0, $cookiejar); $curlResult = DI::httpClient()->get($url, HttpClientAccept::DEFAULT, [HttpClientOptions::COOKIEJAR => $cookiejar]);
if (!$curlResult->isSuccess()) { if (!$curlResult->isSuccess()) {
Logger::debug('mailstream: fetch image url failed', [ Logger::debug('mailstream: fetch image url failed', [
'url' => $url, 'item_id' => $item['id'], 'return_code' => $curlResult->getReturnCode()]); 'url' => $url,
'item_id' => $item['id'],
'return_code' => $curlResult->getReturnCode()
]);
continue; continue;
} }
} catch (InvalidArgumentException $e) { } catch (InvalidArgumentException $e) {
Logger::error('mailstream_do_images exception fetching url', ['url' => $url, 'item_id' => $item['id']]); Logger::error('exception fetching url', ['url' => $url, 'item_id' => $item['id']]);
continue; continue;
} }
$attachments[$url] = [ $attachments[$url] = [
@ -327,13 +309,12 @@ function mailstream_subject(array $item): string
} }
$contact = Contact::selectFirst([], ['id' => $item['contact-id'], 'uid' => $item['uid']]); $contact = Contact::selectFirst([], ['id' => $item['contact-id'], 'uid' => $item['uid']]);
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
Logger::error( Logger::error('no contact', [
'mailstream_subject no contact for item', 'item' => $item['id'],
['id' => $item['id'], 'plink' => $item['plink'],
'plink' => $item['plink'], 'contact id' => $item['contact-id'],
'contact id' => $item['contact-id'], 'uid' => $item['uid']
'uid' => $item['uid']] ]);
);
return DI::l10n()->t("Friendica post"); return DI::l10n()->t("Friendica post");
} }
if ($contact['network'] === 'dfrn') { if ($contact['network'] === 'dfrn') {
@ -370,21 +351,20 @@ function mailstream_subject(array $item): string
function mailstream_send(string $message_id, array $item, array $user): bool function mailstream_send(string $message_id, array $item, array $user): bool
{ {
if (!is_array($item)) { if (!is_array($item)) {
Logger::error('mailstream_send item is empty', ['message_id' => $message_id]); Logger::error('item is empty', ['message_id' => $message_id]);
return false; return false;
} }
if (!$item['visible']) { if (!$item['visible']) {
Logger::debug('mailstream_send item not yet visible', ['item uri' => $item['uri']]); Logger::debug('item not yet visible', ['item uri' => $item['uri']]);
return false; return false;
} }
if (!$message_id) { if (!$message_id) {
Logger::error('mailstream_send no message ID supplied', ['item uri' => $item['uri'], Logger::error('no message ID supplied', ['item uri' => $item['uri'], 'user email' => $user['email']]);
'user email' => $user['email']]);
return true; return true;
} }
require_once (dirname(__file__) . '/phpmailer/class.phpmailer.php'); require_once(dirname(__file__) . '/phpmailer/class.phpmailer.php');
$item['body'] = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']); $item['body'] = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']);
@ -429,21 +409,24 @@ function mailstream_send(string $message_id, array $item, array $user): bool
$item['body'] = BBCode::convertForUriId($item['uri-id'], $item['body'], BBCode::CONNECTORS); $item['body'] = BBCode::convertForUriId($item['uri-id'], $item['body'], BBCode::CONNECTORS);
$item['url'] = DI::baseUrl() . '/display/' . $item['guid']; $item['url'] = DI::baseUrl() . '/display/' . $item['guid'];
$mail->Body = Renderer::replaceMacros($template, [ $mail->Body = Renderer::replaceMacros($template, [
'$upstream' => DI::l10n()->t('Upstream'), '$upstream' => DI::l10n()->t('Upstream'),
'$uri' => DI::l10n()->t('URI'), '$uri' => DI::l10n()->t('URI'),
'$local' => DI::l10n()->t('Local'), '$local' => DI::l10n()->t('Local'),
'$item' => $item]); '$item' => $item
]);
$mail->Body = mailstream_html_wrap($mail->Body); $mail->Body = mailstream_html_wrap($mail->Body);
if (!$mail->Send()) { if (!$mail->Send()) {
throw new Exception($mail->ErrorInfo); throw new Exception($mail->ErrorInfo);
} }
Logger::debug('mailstream_send sent message', ['message ID' => $mail->MessageID, Logger::debug('sent message', [
'subject' => $mail->Subject, 'message ID' => $mail->MessageID,
'address' => $address]); 'subject' => $mail->Subject,
'address' => $address
]);
} catch (phpmailerException $e) { } catch (phpmailerException $e) {
Logger::debug('mailstream_send PHPMailer exception sending message ' . $message_id . ': ' . $e->errorMessage()); Logger::debug('PHPMailer exception sending message', ['id' => $message_id, 'error' => $e->errorMessage()]);
} catch (Exception $e) { } catch (Exception $e) {
Logger::debug('mailstream_send exception sending message ' . $message_id . ': ' . $e->getMessage()); Logger::debug('exception sending message', ['id' => $message_id, 'error' => $e->getMessage()]);
} }
return true; return true;
@ -467,29 +450,6 @@ function mailstream_html_wrap(string &$text)
return $text; return $text;
} }
/**
* Convert v1 mailstream table entries to v2 workerqueue items
*/
function mailstream_convert_table_entries()
{
$ms_item_ids = DBA::selectToArray('mailstream_item', [], ['message-id', 'uri', 'uid', 'contact-id'], ["`mailstream_item`.`completed` IS NULL"]);
Logger::debug('mailstream_convert_table_entries processing ' . count($ms_item_ids) . ' items');
foreach ($ms_item_ids as $ms_item_id) {
$send_hook_data = array('uid' => $ms_item_id['uid'],
'contact-id' => $ms_item_id['contact-id'],
'uri' => $ms_item_id['uri'],
'message_id' => $ms_item_id['message-id'],
'tries' => 0);
if (!$ms_item_id['message-id'] || !strlen($ms_item_id['message-id'])) {
Logger::info('mailstream_convert_table_entries: item has no message-id.', ['item' => $ms_item_id['id'], 'uri' => $ms_item_id['uri']]);
continue;
}
Logger::info('mailstream_convert_table_entries: convert item to workerqueue', $send_hook_data);
Hook::fork(Worker::PRIORITY_LOW, 'mailstream_send_hook', $send_hook_data);
}
DBA::e('DROP TABLE `mailstream_item`');
}
/** /**
* Form for configuring mailstream features for a user * Form for configuring mailstream features for a user
* *

View file

@ -1,5 +1,4 @@
<?php <?php
/* /*
* Name: Mastodon Custom Emojis * Name: Mastodon Custom Emojis
* Description: Replace emojis shortcodes in Mastodon posts with their originating server custom emojis images. * Description: Replace emojis shortcodes in Mastodon posts with their originating server custom emojis images.
@ -9,7 +8,6 @@
* Status: Unsupported * Status: Unsupported
*/ */
use Friendica\App;
use Friendica\Content\Smilies; use Friendica\Content\Smilies;
use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Hook; use Friendica\Core\Hook;
@ -78,7 +76,7 @@ function mastodoncustomemojis_fetch_custom_emojis_for_url($api_base_url)
$api_url = $api_base_url . '/api/v1/custom_emojis'; $api_url = $api_base_url . '/api/v1/custom_emojis';
$fetchResult = DI::httpClient()->fetchFull($api_url); $fetchResult = DI::httpClient()->get($api_url);
if ($fetchResult->isSuccess()) { if ($fetchResult->isSuccess()) {
$emojis_array = json_decode($fetchResult->getBodyString(), true); $emojis_array = json_decode($fetchResult->getBodyString(), true);

213
ratioed/RatioedPanel.php Normal file
View file

@ -0,0 +1,213 @@
<?php
namespace Friendica\Addon\ratioed;
use Friendica\Content\Pager;
use Friendica\Core\Logger;
use Friendica\Core\Renderer;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\User;
use Friendica\Module\Moderation\Users\Active;
/**
* This class implements the "Behaviour" panel in Moderation/Users
*/
class RatioedPanel extends Active
{
protected function content(array $request = []): string
{
Active::content();
if (isset(DI::args()->getArgv()[1]) and DI::args()->getArgv()[1] === 'help') {
$template = Renderer::getMarkupTemplate('/help.tpl', 'addon/ratioed/');
return Renderer::replaceMacros($template, array('$config' => DI::baseUrl() . '/settings/addon'));
}
$action = $this->parameters['action'] ?? '';
$uid = $this->parameters['uid'] ?? 0;
if ($uid) {
$user = User::getById($uid, ['username', 'blocked']);
if (!$user) {
$this->systemMessages->addNotice($this->t('User not found'));
$this->baseUrl->redirect('moderation/users');
}
}
switch ($action) {
case 'delete':
if ($this->session->getLocalUserId() != $uid) {
self::checkFormSecurityTokenRedirectOnError('moderation/users/active', 'moderation_users_active', 't');
// delete user
User::remove($uid);
$this->systemMessages->addNotice($this->t('User "%s" deleted', $user['username']));
} else {
$this->systemMessages->addNotice($this->t('You can\'t remove yourself'));
}
$this->baseUrl->redirect('moderation/users/active');
break;
case 'block':
self::checkFormSecurityTokenRedirectOnError('moderation/users/active', 'moderation_users_active', 't');
User::block($uid);
$this->systemMessages->addNotice($this->t('User "%s" blocked', $user['username']));
$this->baseUrl->redirect('moderation/users/active');
break;
}
$pager = new Pager($this->l10n, $this->args->getQueryString(), 100);
$valid_orders = [
'name',
'email',
'register_date',
'last-activity',
'last-item',
'page-flags',
];
$order = 'last-item';
$order_direction = '-';
if (!empty($request['o'])) {
$new_order = $request['o'];
if ($new_order[0] === '-') {
$order_direction = '-';
$new_order = substr($new_order, 1);
}
if (in_array($new_order, $valid_orders)) {
$order = $new_order;
}
}
$users = User::getList($pager->getStart(), $pager->getItemsPerPage(), 'active', $order, ($order_direction == '-'));
$users = array_map($this->setupUserCallback(), $users);
$header_titles = [
$this->t('Name'),
$this->t('Email'),
$this->t('Register date'),
$this->t('Last login'),
$this->t('Last public item'),
$this->t('Type'),
$this->t('Blocked by'),
$this->t('Comments last 24h'),
$this->t('Reactions last 24h'),
$this->t('Ratio last 24h'),
];
$field_names = [
'name',
'email',
'register_date',
'login_date',
'lastitem_date',
'page_flags',
'blocked_by',
'comments',
'reactions',
'ratio',
];
$th_users = array_map(null, $header_titles, $valid_orders, $field_names);
$count = $this->database->count('user', ["`verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired` AND `uid` != ?", 0]);
$t = Renderer::getMarkupTemplate('ratioed.tpl', 'addon/ratioed');
return self::getTabsHTML('ratioed') . Renderer::replaceMacros($t, [
// strings //
'$title' => $this->t('Moderation'),
'$help_url' => $this->baseUrl . '/ratioed/help',
'$page' => $this->t('Behaviour'),
'$select_all' => $this->t('select all'),
'$delete' => $this->t('Delete'),
'$block' => $this->t('Block'),
'$blocked' => $this->t('User blocked'),
'$siteadmin' => $this->t('Site admin'),
'$accountexpired' => $this->t('Account expired'),
'$h_newuser' => $this->t('Create a new user'),
'$th_users' => $th_users,
'$order_users' => $order,
'$order_direction_users' => $order_direction,
'$confirm_delete_multi' => $this->t('Selected users will be deleted!\n\nEverything these users had posted on this site will be permanently deleted!\n\nAre you sure?'),
'$confirm_delete' => $this->t('The user {0} will be deleted!\n\nEverything this user has posted on this site will be permanently deleted!\n\nAre you sure?'),
'$form_security_token' => self::getFormSecurityToken('moderation_users_active'),
// values //
'$baseurl' => $this->baseUrl,
'$query_string' => $this->args->getQueryString(),
'$users' => $users,
'$count' => $count,
'$pager' => $pager->renderFull($count),
]);
}
protected function setupUserCallback(): \Closure
{
Logger::debug("ratioed: setupUserCallback");
$parentCallback = parent::setupUserCallback();
return function ($user) use ($parentCallback) {
$blocked_count = DBA::count('user-contact', ['uid' => $user['uid'], 'is-blocked' => 1]);
$user['blocked_by'] = $blocked_count;
$self_contact_result = DBA::p('SELECT admin_contact.id AS user_contact_uid FROM contact AS admin_contact JOIN contact AS user_contact ON admin_contact.`uri-id` = user_contact.`uri-id` AND admin_contact.self = 0 AND user_contact.self = 1 WHERE user_contact.uid = ?', $user['uid']);
if (DBA::isResult($self_contact_result)) {
$self_contact_result_row = DBA::fetch($self_contact_result);
$user['user_contact_uid'] = $self_contact_result_row['user_contact_uid'];
}
else {
$user['user_contact_uid'] = NULL;
}
if ($user['user_contact_uid']) {
$post_engagement_result = DBA::p('SELECT SUM(`comments`) AS `comment_count`, SUM(`activities`) AS `activities_count` FROM `post-engagement` WHERE `post-engagement`.created > DATE_SUB(now(), INTERVAL 1 DAY) AND `post-engagement`.`owner-id` = ?', $user['user_contact_uid']);
if (DBA::isResult($post_engagement_result)) {
$post_engagement_result_row = DBA::fetch($post_engagement_result);
$user['comments'] = $post_engagement_result_row['comment_count'];
$user['reactions'] = $post_engagement_result_row['activities_count'];
if ($user['reactions'] > 0) {
$user['ratio'] = number_format($user['comments'] / $user['reactions'], 1, '.', '');
$user['ratioed'] = (float)($user['ratio']) >= 2.0;
}
else {
if ($user['comments'] == 0) {
$user['ratio'] = '0';
$user['ratioed'] = false;
}
else {
$user['ratio'] = '∞';
$user['ratioed'] = false;
}
}
}
else {
$user['comments'] = 'error';
$user['reactions'] = 'error';
$user['ratio'] = 'error';
$user['ratioed'] = false;
}
}
else {
$user['comments'] = 'error';
$user['reactions'] = 'error';
$user['ratio'] = 'error';
$user['ratioed'] = false;
}
$user = $parentCallback($user);
Logger::debug("ratioed: setupUserCallback", [
'uid' => $user['uid'],
'blocked_by' => $user['blocked_by'],
'comments' => $user['comments'],
'reactions' => $user['reactions'],
'ratio' => $user['ratio'],
'ratioed' => $user['ratioed'],
]);
return $user;
};
}
}

58
ratioed/ratioed.php Normal file
View file

@ -0,0 +1,58 @@
<?php
/**
* Name: Ratioed
* Description: Additional moderation user table with statistics about user behaviour
* Version: 0.1
* Author: Matthew Exon <http://mat.exon.name>
*/
use Friendica\Addon\ratioed\RatioedPanel;
use Friendica\Core\Hook;
use Friendica\Core\Logger;
use Friendica\DI;
/**
* Sets up the addon hooks and updates data in the database if needed
*/
function ratioed_install()
{
Hook::register('moderation_users_tabs', 'addon/ratioed/ratioed.php', 'ratioed_users_tabs');
Logger::info("ratioed: installed");
}
/**
* This is a statement rather than an actual function definition. The simple
* existence of this method is checked to figure out if the addon offers a
* module.
*/
function ratioed_module() {}
/**
* @brief Adds additional users tab to the moderation panel
*
* @param array $arr Parameters, including "tabs" which is the list to modify, and "selectedTab", which is the currently selected tab ID
*/
function ratioed_users_tabs(array &$arr) {
Logger::debug("ratioed: users tabs");
array_push($arr['tabs'], [
'label' => DI::l10n()->t('Behaviour'),
'url' => 'ratioed',
'sel' => $arr['selectedTab'] == 'ratioed' ? 'active' : '',
'title' => DI::l10n()->t('Statistics about users behaviour'),
'id' => 'admin-users-ratioed',
'accesskey' => 'r',
]);
}
/**
* @brief Displays the ratioed tab in the moderation panel
*/
function ratioed_content() {
Logger::debug("ratioed: content");
$ratioed = DI::getDice()->create(RatioedPanel::class, [$_SERVER]);
$httpException = DI::getDice()->create(Friendica\Module\Special\HTTPException::class);
$ratioed->run($httpException);
}

View file

@ -0,0 +1,92 @@
<div class="panel 'help-content-wrapper">
<div class="panel-body">
<h2>Ratioed Plugin Help</h2>
<p>
This plugin provides administrators with additional statistics about
the behaviour of users. These may be useful as early warning signs
that warrant more carefully watching the behaviour of a user. They
are <em>not</em> suitable as a trigger for instantly blocking,
muting, or reporting a user, since they lack context.
</p>
<p>
The name of the plugin comes
from <a href="https://knowyourmeme.com/editorials/guides/what-is-the-ratio-and-what-does-it-mean-to-get-ratioed-twitters-1-rule-explained">"The
Ratio"</a>, a well-known quick rule of thumb:
</p>
<blockquote>
If the Replies:RT ratio is greater than 2:1, you done messed up.
</blockquote>
<p>
To "get ratioed" is to receive a large number of comments in a short
space of time, with relatively few likes or boosts. If commenters
were enthusiastic about the posts, they would also have liked or
boosted them. Receiving many comments without such likes or boosts
indicates the comments were probably angry. This anger may or may
not be justified, but either way this is probably something
moderators should be aware of.
</p>
<p>
This plugin allows viewing of an actual ratio, calculated over the
last 24 hours. This is a useful timeframe for sudden dogpiling
events that administrators might not otherwise notice. The plugin
also calculates other statistics.
</p>
<h3>Explanation of Statistics</h3>
<h4>Blocked by</h4>
<p>
This summarises the number of users on remote servers that have
blocked this user.
</p>
<p>
Note that the ActivityPub spec expressly says that
implementations "SHOULD NOT" forward such block messages to
remote servers. Nevertheless some implementations do this
anyway, notably Mastodon. This statistic can only count block
messages from servers that do this, as well as blocks from local
users. As such, it is usually an undercount.
</p>
<p>
The reason the spec recommends against forwarding these messages
is that they can lead to retaliation. For this reason, this
plugin deliberately does not provide any way to investigate
exactly who blocked the user.
</p>
<h4>Comments last 24h</h4>
<p>
This gives the number of comments made on the top-level posts that
this user made within the last 24 hours.
</p>
<h4>Reactions last 24h</h4>
<p>
This collects the number of likes, boosts, or other "one-click"
interactions made on the user's top-level posts within the last 24
hours.
</p>
<h4>Ratio last 24h</h4>
<p>
This is the ratio between "Comments last 24h" and "Reactions last
24h". It is intended to approximate the traditional ratio as
understood on Twitter.
</p>
<h3>Performance</h3>
<p>
The statistics are computed from scratch each time the page loads.
It's possible that this might put a heavy load on the database. and
the page may take a long time to load.
</p>
<h3>Extending</h3>
<p>
Suggestions for additional statistics are welcome, especially from
moderators. This plugin should be considered a sandbox for
experimentation, so it is not necessary to prove that any statistic
is correlated with unwanted behaviour.
</p>
<p>
However, this plugin does deal with potentially sensitive
information. Even if moderators do in principle have access to all
information, it should not necessarily be highlighted. Statistics
should be kept anonymous and neutral. Also, they should be
presented only to moderators, not to the users themselves.
</p>
</div>
</div>

View file

@ -0,0 +1,164 @@
<script type="text/javascript" src="view/theme/frio/js/mod_admin.js?v={{constant('\Friendica\App::VERSION')}}"></script>
<link rel="stylesheet" href="view/theme/frio/css/mod_admin.css?v={{constant('\Friendica\App::VERSION')}}" type="text/css" media="screen"/>
<div id="admin-users" class="adminpage generic-page-wrapper">
<h1>{{$title}} - {{$page}} ({{$count}})</h1>
<p>
<a href="{{$base_url}}/moderation/users/create" class="btn btn-primary"><i class="fa fa-user-plus"></i> {{$h_newuser}}</a>
</p>
<form action="{{$baseurl}}/{{$query_string}}" method="post">
<input type="hidden" name="form_security_token" value="{{$form_security_token}}">
<table id="users" class="table table-hover">
<thead>
<tr>
<th>
<div class="checkbox">
<input type="checkbox" id="admin-settings-users-select" class="selecttoggle" data-select-class="users_ckbx"/>
<label for="admin-settings-users-select"></label>
</div>
</th>
<th></th>
{{foreach $th_users as $k=>$th}}
{{if $k < 2 || $order_users == $th.1 || ($k==5 && !in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1])) }}
<th class="th-{{$k}}">
<a href="{{$baseurl}}/moderation/users/active?o={{if $order_direction_users == "+"}}-{{/if}}{{$th.1}}" class="table-order">
{{if $order_users == $th.1}}
{{if $order_direction_users == "+"}}
&#8595;
{{else}}
&#8593;
{{/if}}
{{else}}
&#8597;
{{/if}}
{{$th.0}}
</a>
</th>
{{/if}}
{{/foreach}}
<th></th>
</tr>
</thead>
<tbody>
{{foreach $users as $u}}
<tr id="user-{{$u.uid}}" class="{{if $u.ratioed}}blocked{{/if}}">
<td>
{{if $u.is_deletable}}
<div class="checkbox">
<input type="checkbox" class="users_ckbx" id="id_user_{{$u.uid}}" name="user[]" value="{{$u.uid}}"/>
<label for="id_user_{{$u.uid}}"></label>
</div>
{{else}}
&nbsp;
{{/if}}
</td>
<td><img class="avatar-nano" src="{{$u.micro}}" title="{{$u.nickname}}"></td>
<td><a href="{{$u.url}}" title="{{$u.nickname}}"> {{$u.name}}</a></td>
<td>{{$u.email}}</td>
{{if $order_users == $th_users.2.1}}
<td>{{$u.register_date}}</td>
{{/if}}
{{if $order_users == $th_users.3.1}}
<td>{{$u.login_date}}</td>
{{/if}}
{{if $order_users == $th_users.4.1}}
<td>{{$u.lastitem_date}}</td>
{{/if}}
{{if !in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1]) }}
<td>
<i class="fa
{{if $u.page_flags_raw==0}}fa-user{{/if}} {{* PAGE_NORMAL *}}
{{if $u.page_flags_raw==1}}fa-bullhorn{{/if}} {{* PAGE_SOAPBOX *}}
{{if $u.page_flags_raw==2}}fa-users{{/if}} {{* PAGE_COMMUNITY *}}
{{if $u.page_flags_raw==3}}fa-heart{{/if}} {{* PAGE_FREELOVE *}}
{{if $u.page_flags_raw==4}}fa-rss{{/if}} {{* PAGE_BLOG *}}
{{if $u.page_flags_raw==5}}fa-user-secret{{/if}} {{* PAGE_PRVGROUP *}}
" title="{{$u.page_flags}}">
</i>
{{if $u.page_flags_raw==0 && $u.account_type_raw > 0}}
<i class="fa
{{if $u.account_type_raw==1}}fa-sitemap{{/if}} {{* ACCOUNT_TYPE_ORGANISATION *}}
{{if $u.account_type_raw==2}}fa-newspaper-o{{/if}} {{* ACCOUNT_TYPE_NEWS *}}
{{if $u.account_type_raw==3}}fa-comments{{/if}} {{* ACCOUNT_TYPE_COMMUNITY *}}
" title="{{$u.account_type}}">
</i>
{{/if}}
{{if $u.is_admin}}<i class="fa fa-user-secret text-primary" title="{{$siteadmin}}"></i>{{/if}}
{{if $u.account_expired}}<i class="fa fa-clock-o text-warning" title="{{$accountexpired}}"></i>{{/if}}
</td>
{{/if}}
<td class="text-right">
<button type="button" class="btn-link admin-settings-action-link" onclick="return details({{$u.uid}})"><span class="caret"></span></button>
</td>
</tr>
<tr id="user-{{$u.uid}}-detail" class=" details hidden {{if $u.blocked != 0}}blocked{{/if}}">
<td>&nbsp;</td>
<td colspan="4">
{{if $order_users != $th_users.2.1}}
<p>
<a href="{{$baseurl}}/ratioed?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.2.1}}" class="btn-link table-order">
&#8597; {{$th_users.2.0}}</a> : {{$u.register_date}}
</p>
{{/if}}
{{if $order_users != $th_users.3.1}}
<p>
<a href="{{$baseurl}}/ratioed?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.3.1}}" class="btn-link table-order">
&#8597; {{$th_users.3.0}}</a> : {{$u.login_date}}
</p>
{{/if}}
{{if $order_users != $th_users.4.1}}
<p>
<a href="{{$baseurl}}/ratioed?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.4.1}}" class="btn-link table-order">
&#8597; {{$th_users.4.0}}</a> : {{$u.lastitem_date}}
</p>
{{/if}}
{{if in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1]) }}
<p>
<a href="{{$baseurl}}/ratioed?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.5.1}}" class="btn-link table-order">
&#8597; {{$th_users.5.0}}</a> : {{$u.page_flags}}{{if $u.page_flags_raw==0 && $u.account_type_raw > 0}}, {{$u.account_type}}{{/if}} {{if $u.is_admin}}({{$siteadmin}}){{/if}} {{if $u.account_expired}}({{$accountexpired}}){{/if}}
</p>
{{/if}}
{{foreach $th_users as $k=>$th}}
{{if $order_users != $th.1 && $k > 5}}
<p>
{{$th.0}} : {{$u[$th.2]}}
</p>
{{/if}}
{{/foreach}}
</td>
<td class="text-right">
{{if $u.is_deletable}}
<a href="{{$baseurl}}/moderation/users/active/block/{{$u.uid}}?t={{$form_security_token}}" class="admin-settings-action-link" title="{{$block}}">
<i class="fa fa-ban" aria-hidden="true"></i>
</a>
<a href="{{$baseurl}}/moderation/users/active/delete/{{$u.uid}}?t={{$form_security_token}}" class="admin-settings-action-link" title="{{$delete}}" onclick="return confirm_delete('{{$confirm_delete}}','{{$u.name}}')">
<i class="fa fa-trash" aria-hidden="true"></i>
</a>
{{else}}
&nbsp;
{{/if}}
</td>
</tr>
{{/foreach}}
</tbody>
</table>
<div class="panel-footer">
<button type="submit" name="page_users_block" value="1" class="btn btn-warning">
<i class="fa fa-ban" aria-hidden="true"></i> {{$block}}
</button>
<button type="submit" name="page_users_delete" value="1" class="btn btn-danger" onclick="return confirm_delete('{{$confirm_delete_multi}}')">
<i class="fa fa-trash" aria-hidden="true"></i> {{$delete}}
</button>
</div>
{{$pager nofilter}}
</form>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 405 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 B

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 908 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View file

@ -2,30 +2,28 @@
/* /*
* Name: Smiley Pack * Name: Smiley Pack
* Description: Pack of smileys that make master too AOLish. * Description: Pack of smileys that make master too AOLish.
* Version: 1.05 * Version: 1.06
* Author: Thomas Willingham (based on Mike Macgirvin's Adult Smile template) * Author: Thomas Willingham (based on Mike Macgirvin's Adult Smile template)
* Author: Matthias Ebers <https://loma.ml/profile/one> * Author: Matthias Ebers <https://loma.ml/profile/one>
* All smileys from sites offering them as Public Domain * All smileys from sites offering them as Public Domain
*/ */
use Friendica\App;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\DI; use Friendica\DI;
function smiley_pack_install() { function smiley_pack_install()
{
Hook::register('smilie', 'addon/smiley_pack/smiley_pack.php', 'smiley_pack_smilies'); Hook::register('smilie', 'addon/smiley_pack/smiley_pack.php', 'smiley_pack_smilies');
} }
function smiley_pack_smilies(array &$b) function smiley_pack_smilies(array &$b)
{ {
#Smileys are split into various directories by the intended range of emotions. This is in case we get too big and need to modularise things. We can then cut and paste the right lines, move the right directory, and just change the name of the addon to happy_smilies or whatever. #Smileys are split into various directories by the intended range of emotions. This is in case we get too big and need to modularise things. We can then cut and paste the right lines, move the right directory, and just change the name of the addon to happy_smilies or whatever.
#Be careful with invocation strings. If you have a smiley called foo, and another called foobar, typing :foobar will call foo. Avoid this with clever naming, using ~ instead of : #Be careful with invocation strings. If you have a smiley called foo, and another called foobar, typing :foobar will call foo. Avoid this with clever naming, using ~ instead of :
#when all else fails. #when all else fails.
#Animal smileys.
#Animal smileys.
$b['texts'][] = ':bunnyflowers:'; $b['texts'][] = ':bunnyflowers:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/animals/bunnyflowers.gif' . '" alt="' . ':bunnyflowers:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/animals/bunnyflowers.gif' . '" alt="' . ':bunnyflowers:' . '" />';
@ -99,9 +97,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':pig:'; $b['texts'][] = ':pig:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/animals/pig.gif' . '" alt="' . ':pig:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/animals/pig.gif' . '" alt="' . ':pig:' . '" />';
#Baby Smileys
#Baby Smileys
$b['texts'][] = ':baby:'; $b['texts'][] = ':baby:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/babies/baby.gif' . '" alt="' . ':baby:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/babies/baby.gif' . '" alt="' . ':baby:' . '" />';
@ -109,15 +105,13 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':babycot:'; $b['texts'][] = ':babycot:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/babies/babycot.gif' . '" alt="' . ':babycot:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/babies/babycot.gif' . '" alt="' . ':babycot:' . '" />';
$b['texts'][] = ':pregnant:'; $b['texts'][] = ':pregnant:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/babies/pregnant.gif' . '" alt="' . ':pregnant:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/babies/pregnant.gif' . '" alt="' . ':pregnant:' . '" />';
$b['texts'][] = ':stork:'; $b['texts'][] = ':stork:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/babies/stork.gif' . '" alt="' . ':stork:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/babies/stork.gif' . '" alt="' . ':stork:' . '" />';
#Confused Smileys
#Confused Smileys
$b['texts'][] = ':confused:'; $b['texts'][] = ':confused:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/confused/confused.gif' . '" alt="' . ':confused:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/confused/confused.gif' . '" alt="' . ':confused:' . '" />';
@ -130,13 +124,12 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':dazed:'; $b['texts'][] = ':dazed:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/confused/dazed.gif' . '" alt="' . ':dazed:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/confused/dazed.gif' . '" alt="' . ':dazed:' . '" />';
#Cool Smileys
#Cool Smileys
$b['texts'][] = ':affro:'; $b['texts'][] = ':affro:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/cool/affro.gif' . '" alt="' . ':affro:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/cool/affro.gif' . '" alt="' . ':affro:' . '" />';
#Devil/Angel Smileys #Devil/Angel Smileys
$b['texts'][] = ':angel:'; $b['texts'][] = ':angel:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/devilangel/angel.gif' . '" alt="' . ':angel:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/devilangel/angel.gif' . '" alt="' . ':angel:' . '" />';
@ -165,7 +158,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':graveside:'; $b['texts'][] = ':graveside:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/devilangel/graveside.gif' . '" alt="' . ':graveside:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/devilangel/graveside.gif' . '" alt="' . ':graveside:' . '" />';
#Unpleasent smileys. #Unpleasent smileys.
$b['texts'][] = ':toilet:'; $b['texts'][] = ':toilet:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/disgust/toilet.gif' . '" alt="' . ':toilet:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/disgust/toilet.gif' . '" alt="' . ':toilet:' . '" />';
@ -176,7 +169,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':fartblush:'; $b['texts'][] = ':fartblush:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/disgust/fartblush.gif' . '" alt="' . ':fartblush:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/disgust/fartblush.gif' . '" alt="' . ':fartblush:' . '" />';
#Drinks #Drinks
$b['texts'][] = ':tea:'; $b['texts'][] = ':tea:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/drink/tea.gif' . '" alt="' . ':tea:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/drink/tea.gif' . '" alt="' . ':tea:' . '" />';
@ -184,7 +177,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':drool:'; $b['texts'][] = ':drool:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/drool/drool.gif' . '" alt="' . ':drool:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/drool/drool.gif' . '" alt="' . ':drool:' . '" />';
#Sad smileys #Sad smileys
$b['texts'][] = ':crying:'; $b['texts'][] = ':crying:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/sad/crying.png' . '" alt="' . ':crying:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/sad/crying.png' . '" alt="' . ':crying:' . '" />';
@ -195,12 +188,12 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':sigh:'; $b['texts'][] = ':sigh:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/sad/sigh.gif' . '" alt="' . ':sigh:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/sad/sigh.gif' . '" alt="' . ':sigh:' . '" />';
#Smoking - only one smiley in here, maybe it needs moving elsewhere? #Smoking - only one smiley in here, maybe it needs moving elsewhere?
$b['texts'][] = ':smoking:'; $b['texts'][] = ':smoking:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/smoking/smoking.gif' . '" alt="' . ':smoking:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/smoking/smoking.gif' . '" alt="' . ':smoking:' . '" />';
#Sport smileys #Sport smileys
$b['texts'][] = ':basketball:'; $b['texts'][] = ':basketball:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/sport/basketball.gif' . '" alt="' . ':basketball:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/sport/basketball.gif' . '" alt="' . ':basketball:' . '" />';
@ -235,7 +228,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':horseriding:'; $b['texts'][] = ':horseriding:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/sport/horseriding.gif' . '" alt="' . ':horseriding:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/sport/horseriding.gif' . '" alt="' . ':horseriding:' . '" />';
#Love smileys #Love smileys
$b['texts'][] = ':iloveyou:'; $b['texts'][] = ':iloveyou:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/love/iloveyou.gif' . '" alt="' . ':iloveyou:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/love/iloveyou.gif' . '" alt="' . ':iloveyou:' . '" />';
@ -255,7 +248,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':loveheart:'; $b['texts'][] = ':loveheart:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/love/loveheart.gif' . '" alt="' . ':loveheart:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/love/loveheart.gif' . '" alt="' . ':loveheart:' . '" />';
#Tired/Sleep smileys #Tired/Sleep smileys
$b['texts'][] = ':countsheep'; $b['texts'][] = ':countsheep';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/tired/countsheep.gif' . '" alt="' . ':countsheep:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/tired/countsheep.gif' . '" alt="' . ':countsheep:' . '" />';
@ -269,7 +262,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':yawn:'; $b['texts'][] = ':yawn:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/tired/yawn.gif' . '" alt="' . ':yawn:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/tired/yawn.gif' . '" alt="' . ':yawn:' . '" />';
#Fight/Flame/Violent smileys #Fight/Flame/Violent smileys
$b['texts'][] = ':2guns:'; $b['texts'][] = ':2guns:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fight/2guns.gif' . '" alt="' . ':2guns:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fight/2guns.gif' . '" alt="' . ':2guns:' . '" />';
@ -313,7 +306,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':acid:'; $b['texts'][] = ':acid:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fight/acid.gif' . '" alt="' . ':acid:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fight/acid.gif' . '" alt="' . ':acid:' . '" />';
#Fantasy smileys - monsters and dragons fantasy. The other type of fantasy belongs in adult smileys #Fantasy smileys - monsters and dragons fantasy. The other type of fantasy belongs in adult smileys
$b['texts'][] = ':alienmonster:'; $b['texts'][] = ':alienmonster:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fantasy/alienmonster.gif' . '" alt="' . ':alienmonster:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fantasy/alienmonster.gif' . '" alt="' . ':alienmonster:' . '" />';
@ -336,7 +329,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':mummy:'; $b['texts'][] = ':mummy:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fantasy/mummy.gif' . '" alt="' . ':mummy:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fantasy/mummy.gif' . '" alt="' . ':mummy:' . '" />';
#Food smileys #Food smileys
$b['texts'][] = ':apple:'; $b['texts'][] = ':apple:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/food/apple.gif' . '" alt="' . ':apple:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/food/apple.gif' . '" alt="' . ':apple:' . '" />';
@ -368,7 +361,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':birthdaycake:'; $b['texts'][] = ':birthdaycake:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/food/birthdaycake.gif' . '" alt="' . ':birthdaycake:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/food/birthdaycake.gif' . '" alt="' . ':birthdaycake:' . '" />';
#Happy smileys #Happy smileys
$b['texts'][] = ':cloud9:'; $b['texts'][] = ':cloud9:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/happy/cloud9.gif' . '" alt="' . ':cloud9:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/happy/cloud9.gif' . '" alt="' . ':cloud9:' . '" />';
@ -376,7 +369,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':tearsofjoy:'; $b['texts'][] = ':tearsofjoy:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/happy/tearsofjoy.gif' . '" alt="' . ':tearsofjoy:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/happy/tearsofjoy.gif' . '" alt="' . ':tearsofjoy:' . '" />';
#Repsect smileys #Repsect smileys
$b['texts'][] = ':bow:'; $b['texts'][] = ':bow:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/respect/bow.gif' . '" alt="' . ':bow:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/respect/bow.gif' . '" alt="' . ':bow:' . '" />';
@ -390,7 +383,19 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':number1:'; $b['texts'][] = ':number1:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/respect/number1.gif' . '" alt="' . ':number1:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/respect/number1.gif' . '" alt="' . ':number1:' . '" />';
#Laugh smileys $b['texts'][] = ':cc_cc:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/respect/cc.png' . '" alt="' . ':cc_cc:' . '" />';
$b['texts'][] = ':cc_by:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/respect/ccby.png' . '" alt="' . ':cc_by:' . '" />';
$b['texts'][] = ':cc_sa:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/respect/ccsa.png' . '" alt="' . ':cc_sa:' . '" />';
$b['texts'][] = ':cc_0:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/respect/cc0.png' . '" alt="' . ':cc_0:' . '" />';
#Laugh smileys
$b['texts'][] = ':hahaha:'; $b['texts'][] = ':hahaha:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/laugh/hahaha.gif' . '" alt="' . ':hahaha:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/laugh/hahaha.gif' . '" alt="' . ':hahaha:' . '" />';
@ -401,24 +406,23 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':rofl:'; $b['texts'][] = ':rofl:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/laugh/rofl.gif' . '" alt="' . ':rofl:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/laugh/rofl.gif' . '" alt="' . ':rofl:' . '" />';
#Music smileys #Music smileys
$b['texts'][] = ':drums:'; $b['texts'][] = ':drums:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/drums.gif' . '" alt="' . ':drums:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/drums.gif' . '" alt="' . ':drums:' . '" />';
$b['texts'][] = ':guitar:'; $b['texts'][] = ':guitar:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/guitar.gif' . '" alt="' . ':guitar:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/guitar.gif' . '" alt="' . ':guitar:' . '" />';
$b['texts'][] = ':trumpet:'; $b['texts'][] = ':trumpet:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/trumpet.gif' . '" alt="' . ':trumpet:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/trumpet.gif' . '" alt="' . ':trumpet:' . '" />';
#Smileys that used to be in core #Smileys that used to be in core
$b['texts'][] = ':headbang:'; $b['texts'][] = ':headbang:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/headbang.gif' . '" alt="' . ':headbang:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/headbang.gif' . '" alt="' . ':headbang:' . '" />';
$b['texts'][] = ':beard:'; $b['texts'][] = ':beard:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/beard.png' . '" alt="' . ':beard:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/beard.png' . '" alt="' . ':beard:' . '" />';
$b['texts'][] = ':whitebeard:'; $b['texts'][] = ':whitebeard:';
@ -436,7 +440,7 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':headdesk:'; $b['texts'][] = ':headdesk:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/headbang.gif' . '" alt="' . ':headdesk:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/headbang.gif' . '" alt="' . ':headdesk:' . '" />';
#These two are still in core, so oldcore isn't strictly right, but we don't want too many directories #These two are still in core, so oldcore isn't strictly right, but we don't want too many directories
$b['texts'][] = ':-d'; $b['texts'][] = ':-d';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/laughing.gif' . '" alt="' . ':-d' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/laughing.gif' . '" alt="' . ':-d' . '" />';
@ -444,8 +448,8 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':-o'; $b['texts'][] = ':-o';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/surprised.gif' . '" alt="' . ':-o' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/oldcore/surprised.gif' . '" alt="' . ':-o' . '" />';
# Regex killers - stick these at the bottom so they appear at the end of the English and # Regex killers - stick these at the bottom so they appear at the end of the English and
# at the start of $OtherLanguage. # at the start of $OtherLanguage.
$b['texts'][] = ':cool:'; $b['texts'][] = ':cool:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/cool/cool.gif' . '" alt="' . ':cool:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/cool/cool.gif' . '" alt="' . ':cool:' . '" />';
@ -480,22 +484,23 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':gangs:'; $b['texts'][] = ':gangs:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fight/gangs.gif' . '" alt="' . ':gangs:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fight/gangs.gif' . '" alt="' . ':gangs:' . '" />';
$b['texts'][] = ':dj:'; $b['texts'][] = ':dj:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/dj.gif' . '" alt="' . ':dj:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/dj.gif' . '" alt="' . ':dj:' . '" />';
$b['texts'][] = ':elvis:'; $b['texts'][] = ':elvis:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/elvis.gif' . '" alt="' . ':elivs:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/elvis.gif' . '" alt="' . ':elivs:' . '" />';
$b['texts'][] = ':violin:'; $b['texts'][] = ':violin:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/violin.gif' . '" alt="' . ':violin:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/music/violin.gif' . '" alt="' . ':violin:' . '" />';
# New Gif Emoji (@one@loma.ml) # New Gif Emoji (@one@loma.ml)
# Fediverse # Fediverse
$b['texts'][] = ':friendica:'; $b['texts'][] = ':friendica:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/friendica.gif' . '" alt="' . ':friendica:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/friendica.png' . '" alt="' . ':friendica:' . '" />';
$b['texts'][] = ':fediverse:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/fediverse.gif' . '" alt="' . ':fediverse:' . '" />';
$b['texts'][] = ':mastodon:'; $b['texts'][] = ':mastodon:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/mastodon.gif' . '" alt="' . ':mastodon:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/mastodon.gif' . '" alt="' . ':mastodon:' . '" />';
@ -507,10 +512,10 @@ function smiley_pack_smilies(array &$b)
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/misskey.gif' . '" alt="' . ':misskey:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/misskey.gif' . '" alt="' . ':misskey:' . '" />';
$b['texts'][] = ':diaspora:'; $b['texts'][] = ':diaspora:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/diaspora.gif' . '" alt="' . ':diaspora:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/diaspora.png' . '" alt="' . ':diaspora:' . '" />';
$b['texts'][] = ':hubzilla:'; $b['texts'][] = ':hubzilla:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/hubzilla.gif' . '" alt="' . ':hubzilla:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/hubzilla.png' . '" alt="' . ':hubzilla:' . '" />';
$b['texts'][] = ':pixelfed:'; $b['texts'][] = ':pixelfed:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/pixelfed.gif' . '" alt="' . ':pixelfeed:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/pixelfed.gif' . '" alt="' . ':pixelfeed:' . '" />';
@ -521,12 +526,12 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':activitypub:'; $b['texts'][] = ':activitypub:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/ap.gif' . '" alt="' . ':activitypub:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/fediverse/ap.gif' . '" alt="' . ':activitypub:' . '" />';
# ccc # ccc
$b['texts'][] = ':ccc event:'; $b['texts'][] = ':ccc event:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/ccc/ccc.gif' . '" alt="' . ':ccc event:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/ccc/ccc.gif' . '" alt="' . ':ccc event:' . '" />';
# Commercial # Commercial
$b['texts'][] = ':youtube:'; $b['texts'][] = ':youtube:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/youtube.gif' . '" alt="' . ':youtube:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/youtube.gif' . '" alt="' . ':youtube:' . '" />';
@ -539,4 +544,107 @@ function smiley_pack_smilies(array &$b)
$b['texts'][] = ':twitch:'; $b['texts'][] = ':twitch:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/twitch.gif' . '" alt="' . ':twitch:' . '" />'; $b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/twitch.gif' . '" alt="' . ':twitch:' . '" />';
$b['texts'][] = ':facebook:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/facebook.gif' . '" alt="' . ':facebook:' . '" />';
$b['texts'][] = ':threads:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/threads.png' . '" alt="' . ':threads:' . '" />';
$b['texts'][] = ':google:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/google.gif' . '" alt="' . ':google:' . '" />';
$b['texts'][] = ':signal:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/signal.gif' . '" alt="' . ':signal:' . '" />';
$b['texts'][] = ':tiktok:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/tiktok.gif' . '" alt="' . ':tiktok:' . '" />';
$b['texts'][] = ':whatsapp:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/whatsapp.gif' . '" alt="' . ':whatsapp:' . '" />';
$b['texts'][] = ':instagram:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/instagram.gif' . '" alt="' . ':instagram:' . '" />';
$b['texts'][] = ':telegram:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/telegram.gif' . '" alt="' . ':telegram:' . '" />';
$b['texts'][] = ':windows:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/windows.png' . '" alt="' . ':windows:' . '" />';
$b['texts'][] = ':github:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/github.png' . '" alt="' . ':github:' . '" />';
$b['texts'][] = ':threema:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/commercial/threema.png' . '" alt="' . ':threema:' . '" />';
# nonCommercial
$b['texts'][] = ':invidious:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/noncommercial/invidious.gif' . '" alt="' . ':invidious:' . '" />';
$b['texts'][] = ':bluesky:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/noncommercial/bluesky.png' . '" alt="' . ':bluesky:' . '" />';
$b['texts'][] = ':vivaldi:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/noncommercial/vivaldi.png' . '" alt="' . ':vivaldi:' . '" />';
# opensource
$b['texts'][] = ':firefox:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/firefox.png' . '" alt="' . ':firefox:' . '" />';
$b['texts'][] = ':linuxopensuse:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/opensuse.png' . '" alt="' . ':linuxopensuse:' . '" />';
$b['texts'][] = ':linuxdebian:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/debian.png' . '" alt="' . ':linuxdebian:' . '" />';
$b['texts'][] = ':linuxfedora:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/fedora.png' . '" alt="' . ':linuxfedora:' . '" />';
$b['texts'][] = ':linuxubuntu:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/ubuntu.png' . '" alt="' . ':linuxubuntu:' . '" />';
$b['texts'][] = ':linuxmint:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/mint.png' . '" alt="' . ':linuxmint:' . '" />';
$b['texts'][] = ':fdroid:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/fdroid.png' . '" alt="' . ':fdroid:' . '" />';
$b['texts'][] = ':tutanota:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/tutanota.png' . '" alt="' . ':tutanota:' . '" />';
$b['texts'][] = ':raspi:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/raspi.png' . '" alt="' . ':raspi:' . '" />';
$b['texts'][] = ':linux:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/linux.png' . '" alt="' . ':linux:' . '" />';
$b['texts'][] = ':kde:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/kde.png' . '" alt="' . ':kde:' . '" />';
$b['texts'][] = ':firefoxnightly:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/firefoxnightly.png' . '" alt="' . ':firefoxnightly:' . '" />';
$b['texts'][] = ':archlinux:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/archlinux.png' . '" alt="' . ':archlinux:' . '" />';
$b['texts'][] = ':thunderbird:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/thunderbird.png' . '" alt="' . ':thunderbird:' . '" />';
$b['texts'][] = ':vivaldi:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/vivaldi.png' . '" alt="' . ':vivaldi:' . '" />';
$b['texts'][] = ':jabber:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/jabber.png' . '" alt="' . ':jabber:' . '" />';
$b['texts'][] = ':matrix:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/matrix.png' . '" alt="' . ':matrix:' . '" />';
$b['texts'][] = ':xmpp:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/xmpp.png' . '" alt="' . ':xmpp:' . '" />';
$b['texts'][] = ':foss:';
$b['icons'][] = '<img class="smiley" src="' . DI::baseUrl() . '/addon/smiley_pack/icons/opensource/foss.png' . '" alt="' . ':foss:' . '" />';
} }

View file

@ -1293,6 +1293,8 @@ function unicode_smilies_smilies(array &$b)
Smilies::add($b, ':microscope:', '&#x1F52C'); Smilies::add($b, ':microscope:', '&#x1F52C');
Smilies::add($b, ':telescope:', '&#x1F52D'); Smilies::add($b, ':telescope:', '&#x1F52D');
Smilies::add($b, ':satellite antenna:', '&#x1F4E1'); Smilies::add($b, ':satellite antenna:', '&#x1F4E1');
Smilies::add($b, ':asterism:', '&#8258');
Smilies::add($b, ':outlines white star:', '&#9885');
// medical // medical
Smilies::add($b, ':syringe:', '&#x1F489'); Smilies::add($b, ':syringe:', '&#x1F489');