* we do not need "Twitter as login". When you've registered the app you get the
* OAuth Consumer key and secret pair for your application/site.
*
- * Add this key pair to your global config/addon.config.php or use the admin panel.
+ * Add this key pair to your config/twitter.config.php file or use the admin panel.
*
- * 'twitter' => [
- * 'consumerkey' => '',
- * 'consumersecret' => '',
- * ],
+ * return [
+ * 'twitter' => [
+ * 'consumerkey' => '',
+ * 'consumersecret' => '',
+ * ],
+ * ];
*
* To activate the addon itself add it to the system.addon
* setting. After this, your user can configure their Twitter account settings
function twitter_load_config(App $a, ConfigFileLoader $loader)
{
- $a->getConfigCache()->load($loader->loadAddonConfig('twitter'));
+ $a->getConfigCache()->load($loader->loadAddonConfig('twitter'), \Friendica\Core\Config\ValueObject\Cache::SOURCE_STATIC);
}
function twitter_check_item_notification(App $a, array &$notification_data)
DI::pConfig()->delete(DI::userSession()->getLocalUserId(), 'twitter', 'mirror_posts');
DI::pConfig()->delete(DI::userSession()->getLocalUserId(), 'twitter', 'import');
DI::pConfig()->delete(DI::userSession()->getLocalUserId(), 'twitter', 'create_user');
+ DI::pConfig()->delete(DI::userSession()->getLocalUserId(), 'twitter', 'auto_follow');
DI::pConfig()->delete(DI::userSession()->getLocalUserId(), 'twitter', 'own_id');
} else {
if (isset($_POST['twitter-pin'])) {
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'twitter', 'mirror_posts', intval($_POST['twitter-mirror']));
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'twitter', 'import', intval($_POST['twitter-import']));
DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'twitter', 'create_user', intval($_POST['twitter-create_user']));
+ DI::pConfig()->set(DI::userSession()->getLocalUserId(), 'twitter', 'auto_follow', intval($_POST['twitter-auto_follow']));
if (!intval($_POST['twitter-mirror'])) {
DI::pConfig()->delete(DI::userSession()->getLocalUserId(), 'twitter', 'lastid');
$mirrorenabled = intval(DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'twitter', 'mirror_posts'));
$importenabled = intval(DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'twitter', 'import'));
$create_userenabled = intval(DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'twitter', 'create_user'));
+ $auto_followenabled = intval(DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'twitter', 'auto_follow'));
// Hide the submit button by default
$submit = '';
'$mirror' => ['twitter-mirror', DI::l10n()->t('Mirror all posts from twitter that are no replies'), $mirrorenabled],
'$import' => ['twitter-import', DI::l10n()->t('Import the remote timeline'), $importenabled],
'$create_user' => ['twitter-create_user', DI::l10n()->t('Automatically create contacts'), $create_userenabled, DI::l10n()->t('This will automatically create a contact in Friendica as soon as you receive a message from an existing contact via the Twitter network. If you do not enable this, you need to manually add those Twitter contacts in Friendica from whom you would like to see posts here.')],
+ '$auto_follow' => ['twitter-auto_follow', DI::l10n()->t('Follow in fediverse'), $auto_followenabled, DI::l10n()->t('Automatically subscribe to the contact in the fediverse, when a fediverse account is mentioned in name or description and we are following the Twitter contact.')],
]);
// Enable the default submit button
return;
}
- if (substr($post['app'], 0, 7) == 'Twitter') {
+ if (substr($post['app'] ?? '', 0, 7) == 'Twitter') {
DI::logger()->info('No Twitter app');
$b['execute'] = false;
return;
}
} else {
// Comments are never exported when we don't import the twitter timeline
- if (!strstr($post['postopts'], 'twitter') || ($post['parent'] != $post['id']) || $post['private']) {
+ if (!strstr($post['postopts'] ?? '', 'twitter') || ($post['parent'] != $post['id']) || $post['private']) {
DI::logger()->info('Comments are never exported when we don\'t import the twitter timeline');
$b['execute'] = false;
return;
return;
}
- $b['body'] = Post\Media::addAttachmentsToBody($b['uri-id'], $b['body']);
+ $b['body'] = Post\Media::addAttachmentsToBody($b['uri-id'], DI::contentItem()->addSharedPost($b));
$thr_parent = null;
Contact::updateAvatar($contact_id, $avatar);
+ if (Contact::isSharing($contact_id, $uid, true) && DI::pConfig()->get($uid, 'twitter', 'auto_follow')) {
+ twitter_auto_follow($uid, $data);
+ }
+
return $contact_id;
}
+/**
+ * Follow a fediverse account that is proived in the name or the profile
+ *
+ * @param integer $uid
+ * @param object $data
+ */
+function twitter_auto_follow(int $uid, object $data)
+{
+ $addrpattern = '([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6})';
+
+ // Search for user@domain.tld in the name
+ if (preg_match('#' . $addrpattern . '#', $data->name, $match)) {
+ if (twitter_add_contact($match[1], true, $uid)) {
+ return;
+ }
+ }
+
+ // Search for @user@domain.tld in the description
+ if (preg_match('#@' . $addrpattern . '#', $data->description, $match)) {
+ if (twitter_add_contact($match[1], true, $uid)) {
+ return;
+ }
+ }
+
+ // Search for user@domain.tld in the description
+ // We don't probe here, since this could be a mail address
+ if (preg_match('#' . $addrpattern . '#', $data->description, $match)) {
+ if (twitter_add_contact($match[1], false, $uid)) {
+ return;
+ }
+ }
+
+ // Search for profile links in the description
+ foreach ($data->entities->description->urls as $url) {
+ if (!empty($url->expanded_url)) {
+ // We only probe on Mastodon style URL to reduce the number of unsuccessful probes
+ twitter_add_contact($url->expanded_url, strpos($url->expanded_url, '@'), $uid);
+ }
+ }
+}
+
+/**
+ * Check if the provided address is a fediverse account and adds it
+ *
+ * @param string $addr
+ * @param boolean $probe
+ * @param integer $uid
+ * @return boolean
+ */
+function twitter_add_contact(string $addr, bool $probe, int $uid): bool
+{
+ $contact = Contact::getByURL($addr, $probe ? null : false, ['id', 'url', 'network']);
+ if (empty($contact)) {
+ Logger::debug('Not a contact address', ['uid' => $uid, 'probe' => $probe, 'addr' => $addr]);
+ return false;
+ }
+
+ if (!in_array($contact['network'], Protocol::FEDERATED)) {
+ Logger::debug('Not a federated network', ['uid' => $uid, 'addr' => $addr, 'contact' => $contact]);
+ return false;
+ }
+
+ if (Contact::isSharing($contact['id'], $uid)) {
+ Logger::debug('Contact has already been added', ['uid' => $uid, 'addr' => $addr, 'contact' => $contact]);
+ return true;
+ }
+
+ Logger::info('Add contact', ['uid' => $uid, 'addr' => $addr, 'contact' => $contact]);
+ Worker::add(Worker::PRIORITY_LOW, 'AddContact', $uid, $contact['url']);
+
+ return true;
+}
+
/**
* @param string $screen_name
* @return stdClass|null