Recherche des tweets de réponse avec l'API Tweeter

This commit is contained in:
hugol
2024-11-17 13:55:25 +01:00
parent 3d386178bf
commit 8dc18553ec
1751 changed files with 379552 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Account;
use Illuminate\Support\Facades\Hash;
class ImportUsersCommand extends Command
{
protected $signature = 'import:users {file}';
protected $description = 'Import users from a text file';
public function __construct()
{
parent::__construct();
}
public function handle()
{
$filePath = $this->argument('file');
if (!file_exists($filePath)) {
$this->error("Le fichier spécifié n'existe pas.");
return 1;
}
$file = fopen($filePath, 'r');
while (($line = fgets($file)) !== false) {
$data = explode(':', trim($line));
if (count($data) < 5) {
$this->error("Format incorrect dans le fichier.");
continue;
}
[$username, $password, $email, $emailPassword, $token] = $data;
Account::create([
'name' => $username,
'password' => $password,
'rambler_email' => $email,
'rambler_password' => $emailPassword,
'auth_token' => $token,
]);
$this->info("Utilisateur {$username} importé avec succès.");
}
fclose($file);
$this->info("Importation terminée.");
return 0;
}
}

View File

@@ -0,0 +1,557 @@
<?php
namespace App\Http\Controllers;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Subscriber\Oauth\Oauth1;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cache;
class APIController extends Controller
{
protected $twitterHeaders = [
'User-Agent' => 'TwitterAndroid/10.46.0-release.0 (310460000-r-0) Android+SDK+built+for+x86/11 (unknown;Android+SDK+built+for+x86;Android;sdk_phone_x86;0;;1;2013)',
'x-twitter-client-language' => 'en-US',
'x-twitter-client-version' => '10.46.0-release.0',
'x-twitter-client' => 'TwitterAndroid',
'x-twitter-api-version' => '5',
'x-twitter-active-user' => 'yes',
'os-version' => '30',
'x-attest-token' => 'no_token',
];
public function retweet($user, $tweetid)
{
$stack = $this->Oauth1($user);
// Créer le client Guzzle avec le handler stack
$client = new Client([
'base_uri' => 'https://api.twitter.com/graphql/',
'handler' => $stack,
'proxy' => [
'http' => 'http://xtjnmwvl-'.$user->id.':lp7iv1lq9glu@p.webshare.io:80',
]
]);
$params = [
"features" => json_encode([
"longform_notetweets_inline_media_enabled" => true,
"super_follow_badge_privacy_enabled" => true,
"longform_notetweets_rich_text_read_enabled" => true,
"super_follow_user_api_enabled" => true,
"super_follow_tweet_api_enabled" => true,
"articles_api_enabled" => true,
"android_graphql_skip_api_media_color_palette" => true,
"creator_subscriptions_tweet_preview_api_enabled" => true,
"freedom_of_speech_not_reach_fetch_enabled" => true,
"tweetypie_unmention_optimization_enabled" => true,
"longform_notetweets_consumption_enabled" => true,
"subscriptions_verification_info_enabled" => true,
"blue_business_profile_image_shape_enabled" => true,
"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled" => true,
"immersive_video_status_linkable_timestamps" => true,
"super_follow_exclusive_tweet_notifications_enabled" => true,
]),
"variables" => json_encode([
"includeTweetImpression" => true,
"includeHasBirdwatchNotes" => false,
"includeEditPerspective" => false,
"tweet_id" => $tweetid,
"includeEditControl" => true,
"includeTweetVisibilityNudge" => true,
])
];
// Effectuer la requête POST avec OAuth et les en-têtes personnalisés
$res = $client->post('24zITFB5aD73PxYtmc6pkA/CreateRetweet', [
'headers' => $this->twitterHeaders, // Ajouter les en-têtes ici
'auth' => 'oauth',
'json' => $params
]);
if ($res->getStatusCode() != 200) {
$text = "Le compte twitter ".$user->Name." a été désactivé";
Http::get('https://api.telegram.org/bot7036172799:AAEVZXHu4SL64VWN5c0AxM1BVxvB0-k6lT0/sendMessage?chat_id=1970698501&text=' . urlencode($text));
$user->enabled = false;
$user->save();
}
}
public function reply($user, $tweetid, $text)
{
$stack = $this->Oauth1($user);
// Créer le client Guzzle avec le handler stack
$client = new Client([
'base_uri' => 'https://api.twitter.com/graphql/',
'handler' => $stack,
'proxy' => [
'http' => 'http://xtjnmwvl-'.$user->id.':lp7iv1lq9glu@p.webshare.io:80',
]
]);
$params = [
"features" => json_encode([
"longform_notetweets_inline_media_enabled" => true,
"super_follow_badge_privacy_enabled" => true,
"longform_notetweets_rich_text_read_enabled" => true,
"super_follow_user_api_enabled" => true,
"super_follow_tweet_api_enabled" => true,
"articles_api_enabled" => true,
"android_graphql_skip_api_media_color_palette" => true,
"creator_subscriptions_tweet_preview_api_enabled" => true,
"freedom_of_speech_not_reach_fetch_enabled" => true,
"tweetypie_unmention_optimization_enabled" => true,
"longform_notetweets_consumption_enabled" => true,
"subscriptions_verification_info_enabled" => true,
"blue_business_profile_image_shape_enabled" => true,
"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled" => true,
"immersive_video_status_linkable_timestamps" => true,
"super_follow_exclusive_tweet_notifications_enabled" => true
]),
"variables" => json_encode([
"nullcast" => false,
"includeTweetImpression" => true,
"includeHasBirdwatchNotes" => false,
"includeEditPerspective" => false,
"includeEditControl" => true,
"batch_compose" => "BatchSubsequent",
"includeCommunityTweetRelationship" => false,
"reply" => [
"exclude_reply_user_ids" => [],
"in_reply_to_tweet_id" => $tweetid,
],
"includeTweetVisibilityNudge" => true,
"tweet_text" => $text,
]),
];
// Effectuer la requête POST avec OAuth et les en-têtes personnalisés
$res = $client->post('2ambiU1P_BmZ_CZ1yQ8E1Q/CreateTweet', [
'headers' => $this->twitterHeaders, // Ajouter les en-têtes ici
'auth' => 'oauth',
'json' => $params
]);
if ($res->getStatusCode() != 200) {
$text = "Le compte twitter ".$user->Name." a été désactivé";
Http::get('https://api.telegram.org/bot7036172799:AAEVZXHu4SL64VWN5c0AxM1BVxvB0-k6lT0/sendMessage?chat_id=1970698501&text=' . urlencode($text));
$user->enabled = false;
$user->save();
}
}
public function follow($user, $follow)
{
$stack = $this->Oauth1($user);
// Créer le client Guzzle avec le handler stack
$client = new Client([
'base_uri' => 'https://api.twitter.com/1.1/',
'handler' => $stack,
'proxy' => [
'http' => 'http://xtjnmwvl-'.$user->id.':lp7iv1lq9glu@p.webshare.io:80',
]
]);
// Effectuer la requête POST avec OAuth et les en-têtes personnalisés
$res = $client->post('friendships/create.json?screen_name='.$follow.'&follow=true', [
'headers' => $this->twitterHeaders, // Ajouter les en-têtes ici
'auth' => 'oauth' // Assurez-vous que l'authentification OAuth est incluse
]);
if ($res->getStatusCode() != 200) {
$text = "Le compte twitter ".$user->Name." a été désactivé";
Http::get('https://api.telegram.org/bot7036172799:AAEVZXHu4SL64VWN5c0AxM1BVxvB0-k6lT0/sendMessage?chat_id=1970698501&text=' . urlencode($text));
$user->enabled = false;
$user->save();
}
}
public function like($user, $tweetid)
{
$stack = $this->Oauth1($user);
// Créer le client Guzzle avec le handler stack
$client = new Client([
'base_uri' => 'https://api.twitter.com/graphql/',
'handler' => $stack,
'proxy' => [
'http' => 'http://xtjnmwvl-'.$user->id.':lp7iv1lq9glu@p.webshare.io:80',
]
]);
$params = [
"variables" => [
"includeTweetImpression" => true,
"includeHasBirdwatchNotes" => false,
"includeEditPerspective" => false,
"tweet_id" => $tweetid,
"includeEditControl" => true
]
];
// Effectuer la requête POST avec OAuth et les en-têtes personnalisés
$res = $client->post('lI07N6Otwv1PhnEgXILM7A/FavoriteTweet', [
'headers' => $this->twitterHeaders, // Ajouter les en-têtes ici
'auth' => 'oauth',
'json' => $params
]);
}
public function tweet($user, $text)
{
$stack = $this->Oauth1($user);
// Créer le client Guzzle avec le handler stack
$client = new Client([
'base_uri' => 'https://api.twitter.com/graphql/',
'handler' => $stack,
'proxy' => [
'http' => 'http://xtjnmwvl-'.$user->id.':lp7iv1lq9glu@p.webshare.io:80',
]
]);
$params = [
"features" => [
"longform_notetweets_inline_media_enabled" => true,
"super_follow_badge_privacy_enabled" => true,
"longform_notetweets_rich_text_read_enabled" => true,
"super_follow_user_api_enabled" => true,
"super_follow_tweet_api_enabled" => true,
"articles_api_enabled" => true,
"android_graphql_skip_api_media_color_palette" => true,
"creator_subscriptions_tweet_preview_api_enabled" => true,
"freedom_of_speech_not_reach_fetch_enabled" => true,
"tweetypie_unmention_optimization_enabled" => true,
"longform_notetweets_consumption_enabled" => true,
"subscriptions_verification_info_enabled" => true,
"blue_business_profile_image_shape_enabled" => true,
"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled" => true,
"immersive_video_status_linkable_timestamps" => true,
"super_follow_exclusive_tweet_notifications_enabled" => true
],
"variables" => [
"nullcast" => false,
"includeTweetImpression" => true,
"includeHasBirdwatchNotes" => false,
"includeEditPerspective" => false,
"includeEditControl" => true,
"includeCommunityTweetRelationship" => false,
"includeTweetVisibilityNudge" => true,
"tweet_text" => $text
]
];
// Effectuer la requête POST avec OAuth et les en-têtes personnalisés
$res = $client->post('2ambiU1P_BmZ_CZ1yQ8E1Q/CreateTweet', [
'headers' => $this->twitterHeaders, // Ajouter les en-têtes ici
'auth' => 'oauth',
'json' => $params
]);
}
public function unread($user)
{
$stack = $this->Oauth1($user);
// Créer le client Guzzle avec le handler stack
$client = new Client([
'base_uri' => 'https://api.twitter.com/2/',
'handler' => $stack,
'proxy' => [
'http' => 'http://xtjnmwvl-'.$user->id.':lp7iv1lq9glu@p.webshare.io:80',
]
]);
// Effectuer la requête POST avec OAuth et les en-têtes personnalisés
$res = $client->get('badge_count/badge_count.json?supports_ntab_urt=true', [
'headers' => $this->twitterHeaders, // Ajouter les en-têtes ici
'auth' => 'oauth' // Assurez-vous que l'authentification OAuth est incluse
]);
$unred = json_decode($res->getBody()->getContents(), true);
// L'URL des deux liens
$url = 'https://myx.ovh/nova/resources/accounts/'.$user->id;
$keyboard = [
'inline_keyboard' => [
[
['text' => 'Cliquez ici pour plus d\'infos', 'url' => $url]
]
]
];
// Convertir le tableau de clavier en JSON
$keyboardJson = json_encode($keyboard);
if ($unred['dm_unread_count'] > 0) {
$text = "Un nouveau message pour le compte ".$user->name;
Http::get('https://api.telegram.org/bot6784810105:AAEq3emnkRwdyvCLC-iqdIjVJ2Ke6HwwGjg/sendMessage', [
'chat_id' => '1970698501', // Remplacez par votre chat_id
'text' => $text,
'reply_markup' => $keyboardJson
]);
}elseif ($unred['ntab_unread_count'] > 0) {
$text = "Une notification pour le compte ".$user->name;
Http::get('https://api.telegram.org/bot6784810105:AAEq3emnkRwdyvCLC-iqdIjVJ2Ke6HwwGjg/sendMessage', [
'chat_id' => '1970698501', // Remplacez par votre chat_id
'text' => $text,
'reply_markup' => $keyboardJson
]);
}
}
public function getweets($user, $tweetid)
{
$stack = $this->Oauth1($user);
// Créer le client Guzzle avec le handler stack
$client = new Client([
'base_uri' => 'https://api.twitter.com/graphql/',
'handler' => $stack,
'proxy' => [
'http' => 'http://xtjnmwvl-'.$user->id.':lp7iv1lq9glu@p.webshare.io:80',
]
]);
$params = [
'variables' => [
'referrer' => 'home',
'includeTweetImpression' => true,
'includeHasBirdwatchNotes' => false,
'isReaderMode' => false,
'includeEditPerspective' => false,
'includeEditControl' => true,
'focalTweetId' => $tweetid,
'includeCommunityTweetRelationship' => true,
'includeTweetVisibilityNudge' => true
],
'features' => [
'longform_notetweets_inline_media_enabled' => true,
'super_follow_badge_privacy_enabled' => true,
'longform_notetweets_rich_text_read_enabled' => true,
'super_follow_user_api_enabled' => true,
'unified_cards_ad_metadata_container_dynamic_card_content_query_enabled' => true,
'super_follow_tweet_api_enabled' => true,
'articles_api_enabled' => true,
'android_graphql_skip_api_media_color_palette' => true,
'creator_subscriptions_tweet_preview_api_enabled' => true,
'freedom_of_speech_not_reach_fetch_enabled' => true,
'tweetypie_unmention_optimization_enabled' => true,
'longform_notetweets_consumption_enabled' => true,
'subscriptions_verification_info_enabled' => true,
'blue_business_profile_image_shape_enabled' => true,
'tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled' => true,
'immersive_video_status_linkable_timestamps' => true,
'super_follow_exclusive_tweet_notifications_enabled' => true
]
];
// Initialisation du tableau des tweets à l'extérieur de la boucle
$tweets = [];
// Initialisation du curseur à null avant le début de la boucle
$cursor = null;
// Nombre de tours
$maxLoops = 10;
// Boucle pour exécuter 5 tours
for ($loop = 1; $loop <= $maxLoops; $loop++) {
// Si ce n'est pas le premier tour, ajouter le curseur dans les paramètres
if ($loop > 1 && $cursor !== null) {
$params['variables']['cursor'] = $cursor; // Ajouter la valeur du curseur au paramètre
}
// Effectuer la requête POST avec OAuth et les en-têtes personnalisés
$res = $client->get('NAHO_rBo3Yf1hBXROp7Msg/ConversationTimelineV2', [
'headers' => $this->twitterHeaders, // Ajouter les en-têtes ici
'auth' => 'oauth',
'json' => $params
]);
// Décoder la réponse
$data = json_decode($res->getBody()->getContents(), true);
// Réinitialiser le curseur à chaque tour
$cursor = null;
// Navigation dans le tableau pour extraire les données
$instructions = $data['data']['timeline_response']['instructions'] ?? [];
foreach ($instructions as $instruction) {
// Vérification si l'instruction est de type 'TimelineAddEntries'
if ($instruction['__typename'] === 'TimelineAddEntries') {
// Parcours des entrées (entries) dans chaque instruction
foreach ($instruction['entries'] as $entry) {
// Vérifier si l'entrée contient un tweet
if (isset($entry['content']['items'][0]['item']['content']['tweetResult'])) {
// Récupérer le texte complet du tweet
$tweets[] = $entry['content']['items'][0]['item']['content']['tweetResult']['result']['legacy']['full_text'] ?? '';
}
// Vérifier si l'entrée contient un curseur, dont l'entryId commence par 'cursor-bottom'
if (isset($entry['content']['content']['value']) && str_starts_with($entry['entryId'], 'cursor-bottom')) {
// Récupérer la valeur du curseur
$cursor = $entry['content']['content']['value'];
}
}
}
}
}
return $tweets;
}
public function newstweet($user)
{
$stack = $this->Oauth1($user);
$newArray = [];
$existingConversationIds = []; // Tableau pour suivre les IDs de conversation déjà vus
$search = [
'1177177387475378177',
'1491472590891888645',
'1642170070351638534',
'1533089245576970240',
'1493931667731406848',
'1513789977938776070',
'1531291630040391682',
'1741188576845455676'
];
foreach ($search as $list) {
// Créer le client Guzzle avec le handler stack
$client = new Client([
'base_uri' => 'https://api.twitter.com/graphql/',
'handler' => $stack,
'proxy' => [
'http' => 'http://xtjnmwvl-'.$user->id.':lp7iv1lq9glu@p.webshare.io:80',
]
]);
// Ajouter les en-têtes personnalisés
$headers = [
'User-Agent' => 'TwitterAndroid/10.46.0-release.0 (310460000-r-0) Android+SDK+built+for+x86/11 (unknown;Android+SDK+built+for+x86;Android;sdk_phone_x86;0;;1;2013)',
'x-twitter-client-language' => 'en-US',
'x-twitter-client-version' => '10.46.0-release.0',
'x-twitter-client' => 'TwitterAndroid',
'x-twitter-api-version' => '5',
'x-twitter-active-user' => 'yes',
'os-version' => '30',
'x-attest-token' => 'no_token',
];
$params = [
"features" => [
"longform_notetweets_inline_media_enabled" => true,
"super_follow_badge_privacy_enabled" => true,
"longform_notetweets_rich_text_read_enabled" => true,
"super_follow_user_api_enabled" => true,
"unified_cards_ad_metadata_container_dynamic_card_content_query_enabled" => true,
"super_follow_tweet_api_enabled" => true,
"articles_api_enabled" => true,
"android_graphql_skip_api_media_color_palette" => true,
"creator_subscriptions_tweet_preview_api_enabled" => true,
"freedom_of_speech_not_reach_fetch_enabled" => true,
"tweetypie_unmention_optimization_enabled" => true,
"longform_notetweets_consumption_enabled" => true,
"subscriptions_verification_info_enabled" => true,
"blue_business_profile_image_shape_enabled" => true,
"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled" => true,
"immersive_video_status_linkable_timestamps" => true,
"super_follow_exclusive_tweet_notifications_enabled" => true,
],
"variables" => [
"includeTweetImpression" => true,
"includeHasBirdwatchNotes" => false,
"includeEditPerspective" => false,
"includeEditControl" => true,
"rest_id" => $list,
"includeTweetVisibilityNudge" => true,
]
];
// Effectuer la requête POST avec OAuth et les en-têtes personnalisés
$res = $client->get('LgWGBhvCVo5vHMsokMdEWA/ListTimeline', [
'headers' => $headers, // Ajouter les en-têtes ici
'auth' => 'oauth',
'json' => $params
]);
$data = json_decode($res->getBody()->getContents(), true);
$tweets = $data['data']['list']['timeline_response']['timeline']['instructions'][0]['entries'];
foreach ($tweets as $item) {
if (isset($item['content']['content']['tweetResult']['result']['legacy']['retweeted_status_result'])) {
// Tweet retweeté, ignorer
} elseif (isset($item['content']['content']['tweetResult']['result']['legacy'])) {
$news = $item['content']['content']['tweetResult']['result']['legacy'];
$conversationId = $news['conversation_id_str'];
// Vérifier si le tweet a déjà été ajouté en fonction de l'ID de la conversation
if (in_array($conversationId, $existingConversationIds)) {
continue; // Si le tweet existe déjà, passer à l'itération suivante
}
// Ajouter l'ID de conversation à la liste des IDs déjà vus
$existingConversationIds[] = $conversationId;
// Ajouter le tweet si le nombre de réponses, de favoris ou de retweets est supérieur à 50
if ($news['reply_count'] > 10 || $news['favorite_count'] > 10 || $news['retweet_count'] > 10) {
$newArray[] = $news;
}
}
}
}
Cache::put('news', $newArray, 1800);
return $newArray;
}
private function Oauth1($user)
{
// Créer le stack pour gérer l'authentification OAuth
$stack = HandlerStack::create();
$middleware = new Oauth1([
'consumer_key' => '3nVuSoBZnx6U4vzUxf5w',
'consumer_secret' => 'Bcs59EFbbsdF6Sl9Ng71smgStWEGwXXKSjYvPVt7qys',
'token' => $user->oauth_token,
'token_secret' => $user->oauth_token_secret
]);
$stack->push($middleware);
return $stack;
}
}

View File

@@ -0,0 +1,341 @@
<?php
namespace App\Http\Controllers;
use App\Jobs\ProcessNews;
use App\Models\Account;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Queue;
use App\Http\Controllers\APIController;
use Illuminate\Support\Facades\Cache;
use Webklex\PHPIMAP\ClientManager;
class AccountController extends Controller
{
public function login($id)
{
$user = Account::find($id);
$username = $user->name;
$password = $user->password;
$email = $user->rambler_email;
$rambler_password = $user->rambler_password;
$proxy = 'http://xtjnmwvl-'.$id.':lp7iv1lq9glu@p.webshare.io:80';
// Clés et jetons de Twitter
$TW_CONSUMER_KEY = '3nVuSoBZnx6U4vzUxf5w';
$TW_CONSUMER_SECRET = 'Bcs59EFbbsdF6Sl9Ng71smgStWEGwXXKSjYvPVt7qys';
$TW_ANDROID_BASIC_TOKEN = 'Basic ' . base64_encode("$TW_CONSUMER_KEY:$TW_CONSUMER_SECRET");
// Requête pour obtenir le bearer token
$response = Http::withHeaders([
'Authorization' => $TW_ANDROID_BASIC_TOKEN,
'Content-Type' => 'application/x-www-form-urlencoded',
])->asForm()->post('https://api.twitter.com/oauth2/token', [
'grant_type' => 'client_credentials',
]);
$bearerToken = collect($response->json())->implode(' ');
// Requête pour obtenir le guest token
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.twitter.com/1.1/guest/activate.json',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => array(
'Authorization: ' . $bearerToken
),
));
$response = curl_exec($curl);
curl_close($curl);
$reponse = json_decode($response, true);
$guestToken = $reponse['guest_token'];
// Configuration des en-têtes pour les futures requêtes
$twitterHeaders = [
'Authorization' => $bearerToken,
'User-Agent' => 'TwitterAndroid/10.46.0-release.0 (310460000-r-0) Android+SDK+built+for+x86/11 (unknown;Android+SDK+built+for+x86;Android;sdk_phone_x86;0;;1;2013)',
'x-twitter-api-version' => '5',
'x-twitter-client' => 'TwitterAndroid',
'x-twitter-client-version' => '10.46.0-release.0',
'x-twitter-active-user' => 'yes',
'os-version' => '30',
'x-twitter-client-language' => 'en-US',
'x-attest-token' => 'no_token',
'X-Guest-Token' => $guestToken,
];
// Initialisation de la session avec Laravel HTTP pour le flux de connexion
// Étape 1 : Commencer le flux de connexion
$task1 = Http::withOptions([
'proxy' => $proxy
])->withHeaders($twitterHeaders)->post('https://api.twitter.com/1.1/onboarding/task.json?flow_name=login&api_version=1&known_device_token=', [
'input_flow_data' => [
'country_code' => null,
'flow_context' => [
'start_location' => [
'location' => 'deeplink',
],
],
'requested_variant' => null,
'target_user_id' => 0,
],
]);
// Stockage de l'en-tête 'att' pour les futures requêtes
$twitterHeaders['att'] = $task1->header('att');
sleep(5);
// Étape 2 : Saisie du nom d'utilisateur
$task2 = Http::withOptions([
'proxy' => $proxy
])->withHeaders($twitterHeaders)->post('https://api.twitter.com/1.1/onboarding/task.json', [
'flow_token' => $task1->json()['flow_token'],
'subtask_inputs' => [
[
'enter_text' => [
'text' => $username,
'link' => 'next_link',
],
'subtask_id' => 'LoginEnterUserIdentifier',
],
],
]);
sleep(5);
if ($task2->json()['subtasks'][0]['subtask_id'] === 'LoginEnterAlternateIdentifierSubtask') {
$task2bis = Http::withOptions([
'proxy' => $proxy
])->withHeaders($twitterHeaders)->post('https://api.twitter.com/1.1/onboarding/task.json', [
'flow_token' => $task2->json()['flow_token'],
'subtask_inputs' => [
[
'enter_text' => [
'text' => $email,
'link' => 'next_link',
],
'subtask_id' => 'LoginEnterAlternateIdentifierSubtask',
],
],
]);
$task2 = $task2bis;
}
sleep(5);
// Étape 3 : Saisie du mot de passe
$task3 = Http::withOptions([
'proxy' => $proxy
])->withHeaders($twitterHeaders)->post('https://api.twitter.com/1.1/onboarding/task.json', [
'flow_token' => $task2->json()['flow_token'],
'subtask_inputs' => [
[
'enter_password' => [
'password' => $password,
'link' => 'next_link',
],
'subtask_id' => 'LoginEnterPassword',
],
],
]);
if ($task3->json()['subtasks'][0]['subtask_id'] === 'LoginAcid') {
sleep(5);
if(isset($task3->json()['subtasks'][0]['enter_text']['hint_text'])){
$code = $this->getCodeFromLastEmail($email, $rambler_password);
$task3bis = Http::withOptions([
'proxy' => $proxy
])->withHeaders($twitterHeaders)->post('https://api.twitter.com/1.1/onboarding/task.json', [
'flow_token' => $task3->json()['flow_token'],
'subtask_inputs' => [
[
'enter_text' => [
'text' => $code,
'link' => 'next_link',
],
'subtask_id' => 'LoginAcid',
],
],
]);
}else{
$task3bis = Http::withOptions([
'proxy' => $proxy
])->withHeaders($twitterHeaders)->post('https://api.twitter.com/1.1/onboarding/task.json', [
'flow_token' => $task3->json()['flow_token'],
'subtask_inputs' => [
[
'enter_text' => [
'text' => $email,
'link' => 'next_link',
],
'subtask_id' => 'LoginAcid',
],
],
]);
}
$task3 = $task3bis;
}
if($task3->json()['subtasks'][0]['subtask_id'] === 'LoginEnterOtp'){
$code = $this->getCodeFromLastEmail($email, $rambler_password);
$task3bis = Http::withOptions([
'proxy' => $proxy
])->withHeaders($twitterHeaders)->post('https://api.twitter.com/1.1/onboarding/task.json', [
'flow_token' => $task3->json()['flow_token'],
'subtask_inputs' => [
[
'enter_text' => [
'text' => $code,
'link' => 'next_link',
],
'subtask_id' => 'LoginEnterOtp',
],
],
]);
$task3 = $task3bis;
}
if($task3->json()['subtasks']['0']['subtask_id'] == 'LoginSuccessSubtask' ){
$user->update([
'oauth_token' => $task3->json()['subtasks']['0']['open_account']['oauth_token'],
'oauth_token_secret' => $task3->json()['subtasks']['0']['open_account']['oauth_token_secret'],
'known_device_token' => $task3->json()['subtasks']['0']['open_account']['known_device_token'],
'enable' => true,
]);
return true;
}else{
return false;
}
}
private function getCodeFromLastEmail($email, $password)
{
// Attendre 15 secondes avant de vérifier les emails
sleep(15);
// Configurer le client IMAP de manière dynamique
$clientManager = new ClientManager();
$client = $clientManager->make([
'host' => 'imap.rambler.ru',
'port' => '993',
'encryption' => 'ssl',
'validate_cert' => true,
'username' => $email,
'password' => $password,
'protocol' => 'imap'
]);
// Se connecter au compte
$client->connect();
// Sélectionner la boîte de réception
$folder = $client->getFolder('INBOX');
// Récupérer le dernier email de l'expéditeur "verify@x.com"
$messages = $folder->messages()->all()->get();
$count = count($messages);
$message = $messages[$count - 1];
if ($message) {
// Vérifier le titre de l'email
$subject = $message->getSubject();
// Chercher le code dans le sujet
if (preg_match('/Your X confirmation code is (\w+)/', $subject, $matches)) {
return $matches[1]; // Retourner le code trouvé dans le titre
}
// Si aucun code trouvé dans le sujet, vérifier le corps du mail
$body = $message->getTextBody();
if (preg_match('/\b(\d{6})\b/', $body, $matches)) {
return $matches[1]; // Retourner le code trouvé dans le corps
}
}
return null;
}
public function tweetnews()
{
// Vérifier si la queue est vide (en fonction de votre driver)
$queue = Queue::getDefaultDriver(); // Par défaut, c'est 'default'
$jobsCount = Queue::size($queue);
if ($jobsCount === 0) {
$accounts = Account::where('enable', true)->get();
foreach($accounts as $user){
$API = new APIController();
//On check les notifs
$API->unread($user);
if (Cache::has('news')) {
$news = Cache::get('news');
}else{
$news = $API->newstweet($user);
}
shuffle($news);
$nb = rand(1,3);
if (count($news) >= $nb) {
$selectedArticles = array_slice($news, 0, $nb);
foreach ($selectedArticles as $article) {
$tweetid = $article['conversation_id_str'];
$API->retweet($user, $tweetid);
sleep(15);
}
}
}
}
}
public
function auto($contestId)
{
$contest = Contest::find($contestId);
if (!$contest) {
session()->flash('error', 'Concours introuvable.');
return;
}
flash()->success('Concours ' . $contest->name . ' en cours de participation');
// Récupération des comptes activés
$accounts = Account::where('enable', true)->get();
foreach ($accounts as $account) {
ProcessTweet::dispatch($contest->id, $account->id); // Envoi du tweet pour participer
}
// Mise à jour du concours pour indiquer qu'il a été participé
$contest->participated = true;
$contest->save();
// Recharger les concours
$this->resetPage(); // Reset pagination to the first page
}
}

View File

@@ -0,0 +1,118 @@
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\TwitterController;
use App\Models\Account;
use App\Models\Block;
use App\Models\Concour;
use App\Models\Contest;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Laravel\Dusk\Browser;
use Symfony\Component\BrowserKit\HttpBrowser;
use Symfony\Component\HttpClient\HttpClient;
use Illuminate\Http\Request;
use Throwable;
class BotController extends Controller
{
public function getSpecialComment($user,$id){
$user = Account::find($user);
$API = new APIController();
// On check les notifs
$texts = $API->getweets($user, $id);
// Filtrer les phrases vides
$texts = array_filter($texts, function($phrase) {
return !empty(trim($phrase)); // Ignore les chaînes vides ou contenant uniquement des espaces
});
// Initialiser un tableau pour stocker les occurrences des phrases
$occurrences = [];
foreach ($texts as $index1 => $phrase1) {
foreach ($texts as $index2 => $phrase2) {
if ($index1 !== $index2) {
$similarity = $this->cosineSimilarity($phrase1, $phrase2);
// Vous pouvez ajuster le seuil de similarité en fonction de vos besoins
if ($similarity > 0.5) {
// Incrémenter le compteur pour les deux phrases
$occurrences[$index1] = isset($occurrences[$index1]) ? $occurrences[$index1] + 1 : 1;
$occurrences[$index2] = isset($occurrences[$index2]) ? $occurrences[$index2] + 1 : 1;
}
}
}
}
// Trouver l'index de la phrase avec le comptage le plus élevé
$indexPhrasePlusFrequente = (!empty($occurrences)) ? array_search(max($occurrences), $occurrences) : null;
// Récupérer la phrase avec le comptage le plus élevé
$phrasePlusFrequente = ($indexPhrasePlusFrequente !== null) ? $texts[$indexPhrasePlusFrequente] : null;
if ($phrasePlusFrequente != null) {
// Supprimer les hashtags
$phrasePlusFrequente = preg_replace('/#\w+\s?/', '', $phrasePlusFrequente);
// Supprimer les tags
$phrasePlusFrequente = preg_replace('/@\w+\s?/', '', $phrasePlusFrequente);
// Supprimer les emojis
$phrasePlusFrequente = $this->remove_emojis($phrasePlusFrequente);
return $phrasePlusFrequente;
} else {
$tweetcomments = config('twitter.sentence_for_random_comment');
return Arr::random($tweetcomments);
}
}
private function cosineSimilarity($text1, $text2)
{
$words1 = str_word_count(strtolower($text1), 1);
$words2 = str_word_count(strtolower($text2), 1);
$allWords = array_unique(array_merge($words1, $words2));
$vector1 = $vector2 = [];
// Construire les vecteurs avec les fréquences des mots
foreach ($allWords as $word) {
$vector1[] = in_array($word, $words1) ? 1 : 0;
$vector2[] = in_array($word, $words2) ? 1 : 0;
}
$dotProduct = 0;
// Calculer le produit scalaire des vecteurs
for ($i = 0; $i < count($allWords); $i++) {
$dotProduct += $vector1[$i] * $vector2[$i];
}
$magnitude1 = sqrt(array_sum($vector1));
$magnitude2 = sqrt(array_sum($vector2));
if ($magnitude1 * $magnitude2 == 0) {
return 0; // Pour éviter une division par zéro
}
return $dotProduct / ($magnitude1 * $magnitude2);
}
private function remove_emojis($string)
{
// Match all emojis (including extended ones)
$regex_emojis = '/[\x{1F600}-\x{1F64F}\x{1F300}-\x{1F5FF}\x{1F680}-\x{1F6FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}]/u';
$clear_string = preg_replace($regex_emojis, '', $string);
return $clear_string;
}
}

View File

@@ -0,0 +1,521 @@
<?php
namespace App\Http\Controllers;
use App\Models\Block;
use App\Models\Contest;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Symfony\Component\BrowserKit\HttpBrowser;
use Symfony\Component\HttpClient\HttpClient;
class ContestController extends Controller
{
public function twitterlist()
{
$client = new HttpBrowser(HttpClient::create(['verify_peer' => false, 'verify_host' => false, 'timeout' => '60']));
// Création des informations d'authentification
$username = 'MyX';
$password = '@Gaudin95';
$credentials = base64_encode("$username:$password");
// Ajout des informations d'authentification à l'en-tête de la demande HTTP
$client->setServerParameter('HTTP_AUTHORIZATION', 'Basic ' . $credentials);
$client->setServerParameter('HTTP_USER_AGENT', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36');
$crawler = $client->request('GET', 'https://nitter-myx.fly.dev/i/lists/1686126753205387264');
$i = 1;
while ($i < 50){
$results = $crawler->filterXPath('//div[@class="timeline-item "]')->each(function ($node) {
$id = ($node->filterXPath('//*[@class="tweet-link"]'))->attr('href');
$created_at = ($node->filterXPath('//span[@class="tweet-date"]/a'))->attr('title');
$text = ($node->filterXPath('//*[@class="tweet-content media-body"]'))->text();
$screen = ($node->filterXPath('//a[@class="username"]'))->text();
$fullname = ($node->filterXPath('//a[@class="fullname"]'))->text();
$imageNode = $node->filterXPath('//a[@class="still-image"]');
$nbretweet = ($node->filterXPath('//*[@class="tweet-stat"][2]'))->text();
$nbretweet = str_replace(',', '', $nbretweet);
$nblike = ($node->filterXPath('//*[@class="tweet-stat"][4]'))->text();
$nblike = str_replace(',', '', $nblike);
$nbreply = ($node->filterXPath('//*[@class="tweet-stat"][1]'))->text();
$nbreply = str_replace(',', '', $nbreply);
//Modifications
if ($imageNode->count() > 0) {
$picture = $imageNode->attr('href');
$picture = urldecode($picture);
$picture = str_replace('/pic/orig/', 'https://pbs.twimg.com/', $picture);
}else{
$picture = null;
}
$screen = str_replace('@', '', $screen);
$id = str_replace('/'.$screen.'/status/', '', $id);
$id = str_replace('#m', '', $id);
$contest = Contest::where('tweetid', $id)->first();
$fake = Block::where('screen_name', $screen)->first();
$containsBlacklistedWord = preg_match('/' . implode('|', array_map('preg_quote', config('twitter.giveaway_to_blacklist'), ['/'])) . '/i', $text) === 1;
if(!$contest && !$fake && $nbretweet > 100 && $containsBlacklistedWord !== true) {
$regex_detect_rts =
[
"/\bRT\b/",
"/RETWEET/i",
"/REPUBL/i",
"/REPOST/i",
];
foreach ($regex_detect_rts as $regex_detect_rt) {
//if (strstr($string, $url)) { // mine version
preg_match($regex_detect_rt, $text, $invites);
if (isset($invites[0])) {
$rt = true;
}
}
//On verifie que si RT
if (isset($rt)) {
$inputs = array();
//On recherche la date de fin du concours
$date = $this->getDate($text);
if ($date != null) {
$fin = $date;
} else {
$fin = $this->getTwitterDate($created_at);
}
//On recherche la date de fin du concours
if ($fin >= Carbon::now()->format('Y-m-d')) {
Contest::create([
'name' => $fullname,
'screen' => $screen,
'description' => $text,
'url' => 'https://x.com/'.$screen.'/status/'.$id,
'picture' => $picture,
'tweetid' => $id,
'fin' => $fin,
'nbretweet' => $nbretweet,
'nblike' => $nblike,
'nbreply' => $nbreply,
]);
}
}
}
});
$nextPageButton = $crawler->filterXPath('//div[@class="show-more"]/a')->link();
$url = $nextPageButton->getUri();
$crawler = $client->request('GET', $url);
$i++;
}
$reponse = $client->getResponse();
var_dump($reponse);
echo 'Fait !';
}
public function searchcontest()
{
$search = [
'giveaway',
'#concours',
'concours like',
'concours rt',
'concours follow',
'#JeuConcours',
'JeuConcours',
'jeu concours',
'offre follow gagnant',
'concours pour gagner',
'gagner rt',
'Gagnez rt follow',
'RT follow',
'concours rt like',
'concours rt fav',
'RT tweet Follow',
'concours rt follow',
'rt follow tas',
'rt follow tirage au sort',
'rt follow gagner',
'rt follow commente',
'rt suivre concours',
'rt suivez concours',
'rt suivre tirage au sort',
'rt suivre tas',
'concours remporter',
'remporter rt',
'concours tas le',
'concours résultats le rt',
'tirage au sort concours',
'concours résultat le rt'
];
$k = array_rand($search);
$phrase = $search[$k];
$date = Carbon::now()->subDays(2)->format('Y-m-d');
$url = 'https://nitter-myx.fly.dev/search?f=tweets&q='.urlencode('min_replies:100 '.$phrase).'&e-nativeretweets=on&e-replies=on&&since='.$date;
$client = new HttpBrowser(HttpClient::create(['verify_peer' => false, 'verify_host' => false, 'timeout' => '60']));
// Création des informations d'authentification
$username = 'MyX';
$password = '@Gaudin95';
$credentials = base64_encode("$username:$password");
// Ajout des informations d'authentification à l'en-tête de la demande HTTP
$client->setServerParameter('HTTP_AUTHORIZATION', 'Basic ' . $credentials);
$client->setServerParameter('HTTP_USER_AGENT', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36');
$crawler = $client->request('GET', $url);
$crawler->filterXPath('//div[@class="timeline-item "]')->each(function ($node) {
$id = ($node->filterXPath('//*[@class="tweet-link"]'))->attr('href');
$created_at = ($node->filterXPath('//span[@class="tweet-date"]/a'))->attr('title');
$text = ($node->filterXPath('//*[@class="tweet-content media-body"]'))->text();
$screen = ($node->filterXPath('//a[@class="username"]'))->text();
$fullname = ($node->filterXPath('//a[@class="fullname"]'))->text();
$imageNode = $node->filterXPath('//a[@class="still-image"]');
$nbretweet = ($node->filterXPath('//*[@class="tweet-stat"][2]'))->text();
$nbretweet = str_replace(',', '', $nbretweet);
$nblike = ($node->filterXPath('//*[@class="tweet-stat"][4]'))->text();
$nblike = str_replace(',', '', $nblike);
$nbreply = ($node->filterXPath('//*[@class="tweet-stat"][1]'))->text();
$nbreply = str_replace(',', '', $nbreply);
//Modifications
if ($imageNode->count() > 0) {
$picture = $imageNode->attr('href');
$picture = urldecode($picture);
$picture = str_replace('/pic/orig/', 'https://pbs.twimg.com/', $picture);
} else {
$picture = null;
}
$screen = str_replace('@', '', $screen);
$id = str_replace('/' . $screen . '/status/', '', $id);
$id = str_replace('#m', '', $id);
$contest = Contest::where('tweetid', $id)->first();
$fake = Block::where('screen_name', $screen)->first();
$containsBlacklistedWord = preg_match('/' . implode('|', array_map('preg_quote', config('twitter.giveaway_to_blacklist'), ['/'])) . '/i', $text) === 1;
if (!$contest && !$fake && $nbretweet > 1000 && $containsBlacklistedWord !== true) {
$regex_detect_rts =
[
"/\bRT\b/",
"/RETWEET/i",
"/REPUBL/i",
"/REPOST/i",
"/LIKE/i",
"/MENTION/i",
];
foreach ($regex_detect_rts as $regex_detect_rt) {
//if (strstr($string, $url)) { // mine version
preg_match($regex_detect_rt, $text, $invites);
if (isset($invites[0])) {
$rt = true;
}
}
//On verifie que si RT
if (isset($rt)) {
$inputs = array();
//On recherche la date de fin du concours
$date = $this->getDate($text);
if ($date != null) {
$fin = $date;
} else {
$fin = $this->getTwitterDate($created_at);
}
//On recherche la date de fin du concours
if ($fin >= Carbon::now()->format('Y-m-d')) {
Contest::create([
'name' => $fullname,
'screen' => $screen,
'description' => $text,
'url' => 'https://x.com/' . $screen . '/status/' . $id,
'picture' => $picture,
'tweetid' => $id,
'fin' => $fin,
'nbretweet' => $nbretweet,
'nblike' => $nblike,
'nbreply' => $nbreply,
]);
}
}
}
});
$reponse = $client->getResponse();
var_dump($reponse);
echo 'Fait !';
}
public function import()
{
$response = Http::get('https://rtandfollow.com/apibot');
$tweets = $response->json();
foreach($tweets as $status) {
$tweetid = $status['tweetid'];
$screen_name = $status['organizer']['screen_name'];
$text = $status['description'];
$concours = Contest::where('tweetid', $tweetid)->first();
$blocking = Block::where('screen_name', $screen_name)->first();
$containsBlacklistedWord = preg_match('/' . implode('|', array_map('preg_quote', config('twitter.giveaway_to_blacklist'), ['/'])) . '/i', $text) === 1;
//On verifie que le concours n'est pas dans la BDD ou que l'utilisateur n'est pas bloqué
if (empty($concours) and empty($blocking) and $containsBlacklistedWord !== true) {
//On recherche la date de fin du concours
$fin = $status['fin'];
if ($fin >= Carbon::now()->format('Y-m-d')) {
Contest::create([
'name' => $status['organizer']['name'],
'screen' => $screen_name,
'description' => $text,
'url' => $status['url'],
'picture' => '',
'tweetid' => $tweetid,
'fin' => $fin,
'nbretweet' => $status['nbretweet'],
'nblike' => $status['nblike'],
'nbreply' => $status['nbreply'],
]);
}
}
}
}
public function getOrganizer($screen)
{
$organiser = Organizer::where('screen_name', $screen)->first();
if ($organiser){
return $organiser->id;
}else
{
$client = new HttpBrowser(HttpClient::create(['verify_peer' => false, 'verify_host' => false, 'timeout' => '60']));
// Création des informations d'authentification
$username = 'autokdo';
$password = '@Gaudin95';
$credentials = base64_encode("$username:$password");
// Ajout des informations d'authentification à l'en-tête de la demande HTTP
$client->setServerParameter('HTTP_AUTHORIZATION', 'Basic ' . $credentials);
$client->setServerParameter('HTTP_USER_AGENT', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36');
$crawler = $client->request('GET', 'https://nitter-autokdo.fly.dev/'.$screen);
try{
$name = $crawler->filterXPath('//*[@class="profile-card-fullname"]')->text();
$description = $crawler->filterXPath('//*[@class="profile-bio"]')->text();
$profileUrl = $crawler->getUri();
$parsedUrl = parse_url($profileUrl);
$baseTwitterUrl = $parsedUrl['scheme'] . '://' . $parsedUrl['host'];
$image = $crawler->filterXPath('//meta[@property="og:image"]')->attr('content');
$url = str_replace("http://nitter-autokdo.fly.dev/pic/", "", $image);
$url = urldecode($url);
$url = 'https://'.$url;
$response = Http::get($url);
$contents = $response->body();
// Utilisation de getimagesize pour obtenir les informations sur l'image
$imageInfo = getimagesizefromstring($contents);
if ($imageInfo === false) {
$filename = 'icon-96x96.png';
}
$format = image_type_to_extension($imageInfo[2], false);
$filename = uniqid() . '.' . $format;
// Stocker l'image dans le répertoire 'ads' du stockage
Storage::put('/public/logo/'.$filename, $contents);
$organiser = Organizer::create([
'name' => $name,
'screen_name' => $screen,
'description' => $description,
'logo' => $filename,
'url' => 'https://twitter.com/'.$screen,
]);
return $organiser->id;
} catch (\Exception $e) {
dd($e);
}
}
}
private function getCategorie($data)
{
$client = new HttpBrowser(HttpClient::create(['verify_peer' => false, 'verify_host' => false, 'timeout' => '60']));
$client->request('GET', 'https://api.uclassify.com/v1/twittercontests/categories/classify/?readKey=c7ivTcoN2ycU&text='.$data);
$json = $client->getResponse()->getContent();
$array = json_decode($json,true);
$laravelArray = collect($array);
$laravelArray = $laravelArray->sortDesc();
$value = $laravelArray->first();
if($value < '0.5'){
$categorie = '20';
}
else{
$cat = $laravelArray->keys()->first();
if($cat == 'argent'){
$categorie = '1';
}elseif($cat == 'beaute'){
$categorie = '2';
}elseif($cat == 'console'){
$categorie = '3';
}elseif($cat == 'cuisine'){
$categorie = '4';
}elseif($cat == 'dvd'){
$categorie = '5';
}elseif($cat == 'enfant'){
$categorie = '6';
}elseif($cat == 'goodies'){
$categorie = '7';
}elseif($cat == 'invitation'){
$categorie = '8';
}elseif($cat == 'livre'){
$categorie = '9';
}elseif($cat == 'maison'){
$categorie = '10';
}elseif($cat == 'mode'){
$categorie = '11';
}elseif($cat == 'pc'){
$categorie = '12';
}elseif($cat == 'sport'){
$categorie = '13';
}elseif($cat == 'telephone'){
$categorie = '14';
}elseif($cat == 'voiture'){
$categorie = '15';
}elseif($cat == 'voyage'){
$categorie = '16';
}else{
$categorie = '20';
}
}
return $categorie;
}
private function getDate($string) {
// Pattern pour détecter les dates au format JJ/MM ou JJ.MM
$pattern_jjmm = '/\b(\d{1,2})(\/|\.)\d{1,2}\b/';
// Pattern pour détecter les dates du style "1 août" (ou autre mois en français)
$mois_fr = array(
'janvier', 'février', 'mars', 'avril', 'mai', 'juin',
'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'
);
$pattern_jj_mois_fr = '/\b(\d{1,2}) (' . implode('|', $mois_fr) . ')\b/i';
// Stocker les correspondances dans un tableau
$correspondances = [];
// Chercher les dates au format JJ/MM ou JJ.MM
if (preg_match_all($pattern_jjmm, $string, $matches)) {
$correspondances = array_merge($correspondances, $matches[0]);
}
// Chercher les dates du style "1 août" (ou autre mois en français)
if (preg_match_all($pattern_jj_mois_fr, $string, $matches)) {
// Convertir le mois en format numérique (1 pour janvier, 2 pour février, etc.)
$mois_numerique = array_flip($mois_fr);
foreach ($matches[2] as $index => $mois) {
$matches[0][$index] = $matches[1][$index] . '/' . str_pad($mois_numerique[strtolower($mois)] + 1, 2, '0', STR_PAD_LEFT);
}
$correspondances = array_merge($correspondances, $matches[0]);
}
// Vérifier s'il y a des dates détectées
if (empty($correspondances)) {
return ""; // Retourne une chaîne vide si aucune date n'est trouvée
}
// Récupérer la dernière date détectée
$derniere_date = end($correspondances);
// Convertir la dernière date au format "Y-m-d" (année-mois-jour)
if (strpos($derniere_date, '/') !== false) {
// Format JJ/MM
list($jour, $mois) = explode('/', $derniere_date);
$annee = date('Y');
} elseif (strpos($derniere_date, '.') !== false) {
// Format JJ.MM
list($jour, $mois) = explode('.', $derniere_date);
$annee = date('Y');
} else {
return ""; // Cas inattendu (ne devrait pas se produire)
}
// Convertir en date avec gestion des erreurs (ex. : février 30)
try {
$date_convertie = date('Y-m-d', strtotime("$annee-$mois-$jour"));
} catch (Exception $e) {
return ""; // Retourne une chaîne vide si la conversion échoue
}
return $date_convertie;
}
private function getTwitterDate($text)
{
// Utilisez une expression régulière pour extraire toutes les dates
$pattern = '/(\w{3} \d{1,2}, \d{4})/';
preg_match_all($pattern, $text, $matches);
if (!empty($matches[1])) {
// Récupérer la dernière date trouvée
$lastDateString = end($matches[1]);
// Analyse de la date
$date = Carbon::createFromFormat('M d, Y', $lastDateString);
// Obtenez la date au format Y-m-d
$formattedDate = $date->addDays(1)->format('Y-m-d');
return $formattedDate;
} else {
// Retourne la date actuelle +1 jour si aucune date n'est trouvée
return Carbon::now()->addDays(1)->format('Y-m-d');
}
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Http\Controllers;
use App\Models\Contest;
use Artesaos\SEOTools\Facades\SEOTools;
class HomeController extends Controller
{
public function index()
{
SEOTools::setTitle('Liste des concours');
return view('index');
}
public function history()
{
$contests = Contest::where('participated',true)->orderby('updated_at','desc')->paginate(20);
SEOTools::setTitle('Mon historique');
return view('history', compact('contests'));
}
}

81
app/Jobs/ProcessNews.php Normal file
View File

@@ -0,0 +1,81 @@
<?php
namespace App\Jobs;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use App\Http\Controllers\APIController;
use App\Models\Account;
use Illuminate\Support\Facades\Cache;
class ProcessNews implements ShouldQueue
{
use Queueable;
private $authid;
/**
* Create a new job instance.
*/
public function __construct($authid)
{
$this->authid= $authid;
}
/**
* Execute the job.
*/
public function handle(): void
{
$API = new APIController();
$user = Account::find($this->authid);
try{
$API->unread($user);
if (Cache::has('news')) {
$news = Cache::get('news');
}else{
$news = $API->newstweet($user);
}
shuffle($news);
$nb = rand(1,3);
if (count($news) >= $nb) {
$selectedArticles = array_slice($news, 0, $nb);
foreach ($selectedArticles as $article) {
$tweetid = $article['conversation_id_str'];
$API->retweet($user, $tweetid);
sleep(15);
}
}
}catch (Exception $exception){
$text = "Le compte Twitter " . $user->name . " : " . $exception->getMessage();
// L'URL des deux liens
$url = 'https://myx.ovh/nova/resources/accounts/'.$user->id;
$keyboard = [
'inline_keyboard' => [
[
['text' => 'Cliquez ici pour plus d\'infos', 'url' => $url]
]
]
];
// Convertir le tableau de clavier en JSON
$keyboardJson = json_encode($keyboard);
// Envoyer le message avec les deux boutons
Http::get('https://api.telegram.org/bot6784810105:AAEq3emnkRwdyvCLC-iqdIjVJ2Ke6HwwGjg/sendMessage', [
'chat_id' => '1970698501',
'text' => $text,
'reply_markup' => $keyboardJson
]);
}
}
}

354
app/Jobs/ProcessTweet.php Normal file
View File

@@ -0,0 +1,354 @@
<?php
namespace App\Jobs;
use App\Http\Controllers\APIController;
use App\Models\Account;
use App\Models\Contest;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Arr;
use Exception;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cache;
class ProcessTweet implements ShouldQueue
{
use Queueable;
public $timeout = 90;
public $tries = 1;
private $id;
private $authid;
/**
* Create a new job instance.
*/
public function __construct($id,$authid)
{
$this->id = $id;
$this->authid= $authid;
}
/**
* Execute the job.
*/
public function handle(): void
{
try{
$user = Account::find($this->authid);
$contest = Contest::findOrFail($this->id);
$text = $contest->description;
$tweetspecial = $this->getSpecialComment($text,$user,$contest->tweetid);
$tweetcomment = $this->getComment($text);
$tags = $this->getTags($text);
$hashtags = $this->getHashtags($text);
$follows = $this->getFollows($text);
$API = new APIController();
//On check les notifs
$API->unread($user);
//On Reweet
$API->retweet($user, $contest->tweetid);
sleep(15);
if (isset($tweetspecial) && isset($tags)) {
$retweet = $tweetspecial . ' ' . $tags . ' ' . $hashtags;
//On reply
$API->reply($user, $contest->tweetid, $retweet);
sleep(15);
}elseif(isset($tweetcomment) && isset($tags)) {
$comments = config('twitter.sentence_for_tag');
$comment = Arr::random($comments);
$retweet = $comment . ' ' . $tags . ' ' . $hashtags;
//On reply
$API->reply($user, $contest->tweetid, $retweet);
sleep(15);
}elseif(isset($tweetspecial)){
$retweet = $tweetspecial . ' ' . $hashtags;
//On reply
$API->reply($user, $contest->tweetid, $retweet);
sleep(15);
}elseif(isset($tweetcomment)){
$comments = config('twitter.sentence_for_random_comment');
$comment = Arr::random($comments);
$retweet = $comment . ' ' . $hashtags;
//On reply
$API->reply($user, $contest->tweetid, $retweet);
sleep(15);
}elseif(isset($tags)){
$retweet = $tags;
//On reply
$API->reply($user, $contest->tweetid, $retweet);
sleep(15);
}
//On follow le créateur
$API->follow($user, $contest->screen);
sleep(15);
if(isset($follows)){
foreach ($follows as $follow){
//On folow les personnes demandées
$API->follow($user, $follow);
sleep(5);
}
}
//On like si besoin
preg_match("/LIKE/i", $text, $like);
if (isset($like[0])) {
//On like le tweet
$API->like($user, $contest->tweetid);
sleep(15);
}
$nb = rand(3,5);
if (Cache::has('news')) {
$news = Cache::get('news');
}else{
$news = $API->newstweet($user);
}
shuffle($news);
$nb = rand(3,5);
if (count($news) >= $nb) {
$selectedArticles = array_slice($news, 0, $nb);
foreach ($selectedArticles as $article) {
$tweetid = $article['conversation_id_str'];
$API->retweet($user, $tweetid);
sleep(15);
}
}
$contest->increment('count');
return;
}catch (Exception $exception){
$text = "Le compte Twitter " . $user->name . " : " . $exception->getMessage();
// L'URL des deux liens
$url = 'https://myx.ovh/nova/resources/accounts/'.$user->id;
$keyboard = [
'inline_keyboard' => [
[
['text' => 'Cliquez ici pour plus d\'infos', 'url' => $url]
]
]
];
// Convertir le tableau de clavier en JSON
$keyboardJson = json_encode($keyboard);
// Envoyer le message avec les deux boutons
Http::get('https://api.telegram.org/bot6784810105:AAEq3emnkRwdyvCLC-iqdIjVJ2Ke6HwwGjg/sendMessage', [
'chat_id' => '1970698501', // Remplacez par votre chat_id
'text' => $text,
'reply_markup' => $keyboardJson
]);
}
}
private function getSpecialComment($text,$user,$id){
//On recherche si une reponse special est attendu
$word_special_comments = config('twitter.word_special_comment');
$combined_regex = implode('|', array_map('preg_quote', $word_special_comments, array_fill(0, count($word_special_comments), '/')));
if (preg_match('/' . $combined_regex . '/i', $text, $matches)) {
try {
$API = new APIController();
// On check les notifs
$texts = $API->getweets($user, $id);
// Filtrer les phrases vides
$texts = array_filter($texts, function($phrase) {
return !empty(trim($phrase)); // Ignore les chaînes vides ou contenant uniquement des espaces
});
// Initialiser un tableau pour stocker les occurrences des phrases
$occurrences = [];
foreach ($texts as $index1 => $phrase1) {
foreach ($texts as $index2 => $phrase2) {
if ($index1 !== $index2) {
$similarity = $this->cosineSimilarity($phrase1, $phrase2);
// Vous pouvez ajuster le seuil de similarité en fonction de vos besoins
if ($similarity > 0.5) {
// Incrémenter le compteur pour les deux phrases
$occurrences[$index1] = isset($occurrences[$index1]) ? $occurrences[$index1] + 1 : 1;
$occurrences[$index2] = isset($occurrences[$index2]) ? $occurrences[$index2] + 1 : 1;
}
}
}
}
// Trouver l'index de la phrase avec le comptage le plus élevé
$indexPhrasePlusFrequente = (!empty($occurrences)) ? array_search(max($occurrences), $occurrences) : null;
// Récupérer la phrase avec le comptage le plus élevé
$phrasePlusFrequente = ($indexPhrasePlusFrequente !== null) ? $texts[$indexPhrasePlusFrequente] : null;
if ($phrasePlusFrequente != null) {
// Supprimer les hashtags
$phrasePlusFrequente = preg_replace('/#\w+\s?/', '', $phrasePlusFrequente);
// Supprimer les tags
$phrasePlusFrequente = preg_replace('/@\w+\s?/', '', $phrasePlusFrequente);
// Supprimer les emojis
$phrasePlusFrequente = $this->remove_emojis($phrasePlusFrequente);
return $phrasePlusFrequente;
} else {
$tweetcomments = config('twitter.sentence_for_random_comment');
return Arr::random($tweetcomments);
}
} catch (\Exception $e) {
$tweetcomments = config('twitter.sentence_for_random_comment');
return Arr::random($tweetcomments);
}
}
}
private function getComment($text){
//On recherche si un commentaire est attendu
$word_comments = config('twitter.word_comment');
$combined_regex = implode('|', array_map('preg_quote', $word_comments, array_fill(0, count($word_comments), '/')));
if (preg_match('/' . $combined_regex . '/i', $text, $matches)) {
return true;
}
}
private function getTags($text){
//On recherche si ça demande un tag
$word_tags = config('twitter.word_tag');
$combined_regex = implode('|', array_map('preg_quote', $word_tags, array_fill(0, count($word_tags), '/')));
if (preg_match('/' . $combined_regex . '/i', $text, $matches)) {
//On recherche si ça demande 1 tag
$one_people_lists = config('twitter.one_people_list');
$combined_regex = implode('|', array_map('preg_quote', $one_people_lists, array_fill(0, count($one_people_lists), '/')));
if (preg_match('/' . $combined_regex . '/i', $text, $matches)) {
return '@chauchettes';
}
//On recherche si ça demande 2 tags
$two_people_lists = config('twitter.two_people_list');
$combined_regex = implode('|', array_map('preg_quote', $two_people_lists, array_fill(0, count($two_people_lists), '/')));
if (preg_match('/' . $combined_regex . '/i', $text, $matches)) {
return '@chauchettes @totorunior';
}
//On recherche si ça demande 3 tags ou plus
$three_or_more_people_lists = config('twitter.three_or_more_people_list');
$combined_regex = implode('|', array_map('preg_quote', $three_or_more_people_lists, array_fill(0, count($three_or_more_people_lists), '/')));
if (preg_match('/' . $combined_regex . '/i', $text, $matches)) {
return '@chauchettes @totorunior @crakotte84';
}
}
}
private function getHashtags($text){
preg_match_all("/#[a-zA-Z0-9]+/", $text, $hashtags);
if (isset($hashtags[0])) {
$hashtags = array_unique($hashtags[0]);
$blacklist = config('twitter.hashtag_to_blacklist');
// Comparer les deux tableaux et supprimer les correspondances du premier tableau
$resultLower = array_udiff($hashtags, $blacklist, 'strcasecmp');
// Construire la chaîne finale des hashtags restants
$final = implode(" ", $resultLower);
return $final;
}
else{
return '';
}
}
private function cosineSimilarity($text1, $text2)
{
$words1 = str_word_count(strtolower($text1), 1);
$words2 = str_word_count(strtolower($text2), 1);
$allWords = array_unique(array_merge($words1, $words2));
$vector1 = $vector2 = [];
// Construire les vecteurs avec les fréquences des mots
foreach ($allWords as $word) {
$vector1[] = in_array($word, $words1) ? 1 : 0;
$vector2[] = in_array($word, $words2) ? 1 : 0;
}
$dotProduct = 0;
// Calculer le produit scalaire des vecteurs
for ($i = 0; $i < count($allWords); $i++) {
$dotProduct += $vector1[$i] * $vector2[$i];
}
$magnitude1 = sqrt(array_sum($vector1));
$magnitude2 = sqrt(array_sum($vector2));
if ($magnitude1 * $magnitude2 == 0) {
return 0; // Pour éviter une division par zéro
}
return $dotProduct / ($magnitude1 * $magnitude2);
}
private function getFollows(mixed $text)
{
preg_match_all("/\s@([\w_-]+)/", $text, $mentions);
if(isset($mentions[1])){
$mentions = array_unique($mentions[1]);
return $mentions;
}else{
return '';
}
}
private function remove_emojis($string)
{
// Match all emojis (including extended ones)
$regex_emojis = '/[\x{1F600}-\x{1F64F}\x{1F300}-\x{1F5FF}\x{1F680}-\x{1F6FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}]/u';
$clear_string = preg_replace($regex_emojis, '', $string);
return $clear_string;
}
}

79
app/Livewire/Contests.php Normal file
View File

@@ -0,0 +1,79 @@
<?php
namespace App\Livewire;
use App\Models\Contest;
use App\Models\Account;
use App\Jobs\ProcessTweet;
use Carbon\Carbon;
use Livewire\Component;
use Livewire\WithPagination;
class Contests extends Component
{
use WithPagination;
// Fonction appelée lors du chargement du composant
public function mount()
{
// Initial setup if needed
}
// Fonction pour participer automatiquement à un concours
public function auto($contestId)
{
$contest = Contest::find($contestId);
if (!$contest) {
session()->flash('error', 'Concours introuvable.');
return;
}
flash()->success('Concours '.$contest->name.' en cours de participation');
// Récupération des comptes activés
$accounts = Account::where('enable', true)->get();
foreach ($accounts as $account) {
ProcessTweet::dispatch($contest->id, $account->id); // Envoi du tweet pour participer
}
// Mise à jour du concours pour indiquer qu'il a été participé
$contest->participated = true;
$contest->save();
// Recharger les concours
$this->resetPage(); // Reset pagination to the first page
}
// Fonction pour supprimer un concours
public function delete($contestId)
{
$contest = Contest::find($contestId);
if ($contest) {
$contest->enable = false;
$contest->save();
flash()->error('Concours suppimé');
}
// Recharger les concours après la suppression
$this->resetPage(); // Reset pagination to the first page
}
// Fonction pour afficher la vue
public function render()
{
$datefin = Carbon::now()->addDays(3)->format('Y-m-d');
$contests = Contest::where('fin', '>=', now())
->where('fin', '<=', $datefin)
->where('participated', '!=', true)
//->where('enable', true)
->orderBy('fin', 'asc')
->paginate(20);
return view('livewire.contests', [
'contests' => $contests
]);
}
}

18
app/Models/Account.php Normal file
View File

@@ -0,0 +1,18 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Account extends Model
{
use HasFactory;
public $casts = [
'password' => 'encrypted',
'cookies' => 'encrypted',
];
protected $guarded = ['id'];
}

13
app/Models/Block.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Block extends Model
{
use HasFactory;
protected $guarded = ['id'];
}

18
app/Models/Category.php Normal file
View File

@@ -0,0 +1,18 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use HasFactory;
protected $guarded = [];
public function contests()
{
return $this->hasMany(Contest::class);
}
}

13
app/Models/Concour.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Concour extends Model
{
use HasFactory;
protected $guarded = ['id'];
}

26
app/Models/Contest.php Normal file
View File

@@ -0,0 +1,26 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Contest extends Model
{
use HasFactory;
protected $guarded = [];
protected $casts = [
'fin' => 'date',
];
public function category()
{
return $this->belongsTo(Category::class);
}
public function organizer()
{
return $this->belongsTo(Organizer::class);
}
}

13
app/Models/Hashtag.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Hashtag extends Model
{
use HasFactory;
protected $guarded = ['id'];
}

18
app/Models/Organizer.php Normal file
View File

@@ -0,0 +1,18 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Organizer extends Model
{
use HasFactory;
protected $guarded = [];
public function contests()
{
return $this->hasMany(Contest::class);
}
}

13
app/Models/Sentence.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Sentence extends Model
{
use HasFactory;
protected $guarded = ['id'];
}

114
app/Nova/Account.php Normal file
View File

@@ -0,0 +1,114 @@
<?php
namespace App\Nova;
use App\Nova\Actions\AutoLogin;
use App\Nova\Actions\Connexion;
use App\Nova\Actions\Webmail;
use App\Nova\Filters\EnableFilter;
use Illuminate\Http\Request;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Password;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Fields\Textarea;
use Laravel\Nova\Fields\Boolean;
use Laravel\Nova\Http\Requests\NovaRequest;
class Account extends Resource
{
/**
* The model the resource corresponds to.
*
* @var class-string<\App\Models\Account>
*/
public static $model = \App\Models\Account::class;
/**
* The single value that should be used to represent the resource when being displayed.
*
* @var string
*/
public static $title = 'id';
/**
* The columns that should be searched.
*
* @var array
*/
public static $search = [
'id',
];
/**
* Get the fields displayed by the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [
ID::make()->sortable(),
Boolean::make('Enable'),
Text::make('Name'),
Text::make('Password'),
Text::make('Rambler Email')->readonly(),
Text::make('Rambler Password')->readonly()->hideFromIndex(),
Text::make('Auth Token')->hideFromIndex(),
Text::make('Oauth Token')->readonly()->hideFromIndex(),
Text::make('Oauth Token Secret')->readonly()->hideFromIndex(),
Text::make('Known Device Token')->readonly()->hideFromIndex(),
];
}
/**
* Get the cards available for the request.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function cards(NovaRequest $request)
{
return [];
}
/**
* Get the filters available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function filters(NovaRequest $request)
{
return [
new EnableFilter(),
];
}
/**
* Get the lenses available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function lenses(NovaRequest $request)
{
return [];
}
/**
* Get the actions available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function actions(NovaRequest $request)
{
return [
Connexion::make(),
AutoLogin::make(),
Webmail::make(),
];
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Nova\Actions;
use foroco\BrowserDetection;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Collection;
use Laravel\Nova\Actions\Action;
use Laravel\Nova\Fields\ActionFields;
use Laravel\Nova\Http\Requests\NovaRequest;
use Lexicon\ActionButtonSelector\ActionAsButton;
class AutoLogin extends Action
{
use InteractsWithQueue, Queueable;
use ActionAsButton;
/**
* Perform the action on the given models.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Support\Collection $models
* @return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
$Browser = new BrowserDetection();
$useragent = $_SERVER['HTTP_USER_AGENT'];
if (str_contains($Browser->getOS($useragent)['os_family'], 'android')) {
// Générer un lien spécifique pour Kiwi
$kiwiLink = 'intent://x.com?auth_token='.$models->first()->auth_token.'#Intent;package=com.kiwibrowser.browser;scheme=https;end';
return Action::redirect($kiwiLink);
}
return Action::openInNewTab('https://x.com?auth_token='.$models->first()->auth_token);
}
/**
* Get the fields available on the action.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [];
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace App\Nova\Actions;
use App\Http\Controllers\AccountController;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Collection;
use Laravel\Nova\Actions\Action;
use Laravel\Nova\Fields\ActionFields;
use Laravel\Nova\Http\Requests\NovaRequest;
use Lexicon\ActionButtonSelector\ActionAsButton;
use App\Http\Controllers\TwitterController;
class Connexion extends Action
{
use InteractsWithQueue, Queueable;
use ActionAsButton;
/**
* Perform the action on the given models.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Support\Collection $models
* @return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
//return Action::openInNewTab('/login/'.$models->first()->id);
$connexion = (new AccountController)->login($models->first()->id);
if($connexion){
return Action::message('Compte synchronisé');
}elseif(!$connexion){
return Action::message('Erreur de connexion');
}else{
return $connexion;
}
}
/**
* Get the fields available on the action.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [];
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace App\Nova\Actions;
use App\Models\Contest;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Collection;
use Laravel\Nova\Actions\Action;
use Laravel\Nova\Fields\ActionFields;
use Laravel\Nova\Http\Requests\NovaRequest;
use Lexicon\ActionButtonSelector\ActionAsButton;
use App\Http\Controllers\TwitterController;
class Participer extends Action
{
use InteractsWithQueue, Queueable;
use ActionAsButton;
public function __construct()
{
$this->withoutConfirmation();
}
/**
* Perform the action on the given models.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Support\Collection $models
* @return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
if($models->first()->participated == 1){
Action::danger('Vous avez déjà participé');
}
else{
$contest = Contest::find($models->first()->id);
$contest->participated = 1;
$contest->save();
Action::message('Participation en cours');
}
}
/**
* Get the fields available on the action.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [];
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Nova\Actions;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Collection;
use Laravel\Nova\Actions\Action;
use Laravel\Nova\Fields\ActionFields;
use Laravel\Nova\Http\Requests\NovaRequest;
use Lexicon\ActionButtonSelector\ActionAsButton;
class Webmail extends Action
{
use InteractsWithQueue, Queueable;
use ActionAsButton;
/**
* Perform the action on the given models.
*
* @param \Laravel\Nova\Fields\ActionFields $fields
* @param \Illuminate\Support\Collection $models
* @return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
$mail = urlencode($models->first()->rambler_email);
$password = urlencode($models->first()->rambler_password);
return Action::openInNewTab('https://mail.myx.ovh/?postlogin&Email='.$mail.'&Password='.$password);
}
/**
* Get the fields available on the action.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [];
}
}

92
app/Nova/Block.php Normal file
View File

@@ -0,0 +1,92 @@
<?php
namespace App\Nova;
use Illuminate\Http\Request;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Http\Requests\NovaRequest;
class Block extends Resource
{
/**
* The model the resource corresponds to.
*
* @var class-string<\App\Models\Block>
*/
public static $model = \App\Models\Block::class;
/**
* The single value that should be used to represent the resource when being displayed.
*
* @var string
*/
public static $title = 'screen_name';
/**
* The columns that should be searched.
*
* @var array
*/
public static $search = [
'screen_name',
];
/**
* Get the fields displayed by the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [
ID::make()->sortable(),
Text::make('screen_name'),
];
}
/**
* Get the cards available for the request.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function cards(NovaRequest $request)
{
return [];
}
/**
* Get the filters available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function filters(NovaRequest $request)
{
return [];
}
/**
* Get the lenses available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function lenses(NovaRequest $request)
{
return [];
}
/**
* Get the actions available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function actions(NovaRequest $request)
{
return [];
}
}

121
app/Nova/Contest.php Normal file
View File

@@ -0,0 +1,121 @@
<?php
namespace App\Nova;
use App\Nova\Actions\Participer;
use Illuminate\Http\Request;
use Laravel\Nova\Fields\Boolean;
use Laravel\Nova\Fields\Date;
use Laravel\Nova\Fields\DateTime;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Image;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Fields\Textarea;
use Laravel\Nova\Fields\URL;
use Laravel\Nova\Http\Requests\NovaRequest;
class Contest extends Resource
{
/**
* The model the resource corresponds to.
*
* @var class-string<\App\Models\Contest>
*/
public static $model = \App\Models\Contest::class;
/**
* The single value that should be used to represent the resource when being displayed.
*
* @var string
*/
public static $title = 'name';
/**
* The columns that should be searched.
*
* @var array
*/
public static $search = [
'name',
];
public static function indexQuery(NovaRequest $request, $query): \Illuminate\Database\Eloquent\Builder
{
return $query->where('fin', '>=', now())->where('participated', '!=', true);
}
/**
* Get the fields displayed by the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [
Boolean::make('Participate', 'participated'),
Text::make('Name')->readonly(),
Text::make('Screen')->readonly(),
Text::make('Description')->readonly(),
Text::make('Nb Like', 'nblike')->readonly(),
Text::make('Nb Reply', 'nbreply')->readonly(),
Image::make('Image', 'picture')
->thumbnail(function ($value) {
return $value;
})
->preview(function ($value) {
return $value;
})
->disableDownload(),
URL::make('URL')->readonly(),
Date::make('Fin')->readonly()->sortable(),
Text::make('Tweet ID', 'tweetid')->hideFromIndex()->readonly(),
Text::make('Nb Tweet', 'nbtweet')->readonly(),
];
}
/**
* Get the cards available for the request.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function cards(NovaRequest $request)
{
return [];
}
/**
* Get the filters available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function filters(NovaRequest $request)
{
return [];
}
/**
* Get the lenses available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function lenses(NovaRequest $request)
{
return [];
}
/**
* Get the actions available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function actions(NovaRequest $request)
{
return [
Participer::make(),
];
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Nova\Dashboards;
use Laravel\Nova\Cards\Help;
use Laravel\Nova\Dashboards\Main as Dashboard;
class Main extends Dashboard
{
/**
* Get the cards for the dashboard.
*
* @return array
*/
public function cards()
{
return [
new Help,
];
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Nova\Filters;
use Illuminate\Http\Request;
use Laravel\Nova\Filters\Boolean;
use Laravel\Nova\Filters\Filter;
class EnableFilter extends Filter
{
public $name = 'Enabled Filter';
/**
* Apply the filter to the given resource query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $value
* @return \Illuminate\Database\Eloquent\Builder
*/
public function apply(Request $request, $query, $value)
{
return $query->where('enable', $value);
}
/**
* Get the filter's available options.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function options(Request $request)
{
return [
'Enabled' => 1,
'Disabled' => 0,
];
}
}

92
app/Nova/Hashtag.php Normal file
View File

@@ -0,0 +1,92 @@
<?php
namespace App\Nova;
use Illuminate\Http\Request;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Http\Requests\NovaRequest;
class Hashtag extends Resource
{
/**
* The model the resource corresponds to.
*
* @var class-string<\App\Models\Hashtag>
*/
public static $model = \App\Models\Hashtag::class;
/**
* The single value that should be used to represent the resource when being displayed.
*
* @var string
*/
public static $title = 'name';
/**
* The columns that should be searched.
*
* @var array
*/
public static $search = [
'name',
];
/**
* Get the fields displayed by the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [
ID::make()->sortable(),
Text::make('Name'),
];
}
/**
* Get the cards available for the request.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function cards(NovaRequest $request)
{
return [];
}
/**
* Get the filters available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function filters(NovaRequest $request)
{
return [];
}
/**
* Get the lenses available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function lenses(NovaRequest $request)
{
return [];
}
/**
* Get the actions available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function actions(NovaRequest $request)
{
return [];
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace App\Nova\Metrics;
use App\Models\Concour;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Metrics\Trend;
class ConcoursPerDay extends Trend
{
/**
* Calculate the value of the metric.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return mixed
*/
public function calculate(NovaRequest $request)
{
return $this->countByDays($request, Concour::class);
}
/**
* Get the ranges available for the metric.
*
* @return array
*/
public function ranges()
{
return [
7 => __('7 Jours'),
14 => __('14 Jours'),
30 => __('30 Jours'),
60 => __('60 Jours'),
90 => __('90 Jours'),
];
}
/**
* Determine the amount of time the results of the metric should be cached.
*
* @return \DateTimeInterface|\DateInterval|float|int|null
*/
public function cacheFor()
{
// return now()->addMinutes(5);
}
/**
* Get the URI key for the metric.
*
* @return string
*/
public function uriKey()
{
return 'concours-per-day';
}
public function name()
{
return 'Tendances des Concours';
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace App\Nova\Metrics;
use App\Models\Concour;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Metrics\Value;
class NewConcours extends Value
{
/**
* Calculate the value of the metric.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return mixed
*/
public function calculate(NovaRequest $request)
{
return $this->count($request, Concour::class);
}
/**
* Get the ranges available for the metric.
*
* @return array
*/
public function ranges()
{
return [
'TODAY' => __('Aujourd\'hui'),
30 => __('30 Jours'),
60 => __('60 Jours'),
365 => __('365 Jours'),
'MTD' => __('Mois à date'),
'QTD' => __('Trimestre à date'),
'YTD' => __('Année à date'),
];
}
/**
* Determine the amount of time the results of the metric should be cached.
*
* @return \DateTimeInterface|\DateInterval|float|int|null
*/
public function cacheFor()
{
// return now()->addMinutes(5);
}
public function name()
{
return 'Concours Participés';
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace App\Nova\Metrics;
use App\Models\Concour;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Metrics\Partition;
class UserPerConcours extends Partition
{
/**
* Calculate the value of the metric.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return mixed
*/
public function calculate(NovaRequest $request)
{
return $this->count($request, Concour::class, 'description');
}
/**
* Determine the amount of time the results of the metric should be cached.
*
* @return \DateTimeInterface|\DateInterval|float|int|null
*/
public function cacheFor()
{
// return now()->addMinutes(5);
}
/**
* Get the URI key for the metric.
*
* @return string
*/
public function uriKey()
{
return 'user-per-concours';
}
public function name()
{
return 'Organisateurs';
}
}

59
app/Nova/Resource.php Normal file
View File

@@ -0,0 +1,59 @@
<?php
namespace App\Nova;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Resource as NovaResource;
abstract class Resource extends NovaResource
{
/**
* Build an "index" query for the given resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public static function indexQuery(NovaRequest $request, $query)
{
return $query;
}
/**
* Build a Scout search query for the given resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Laravel\Scout\Builder $query
* @return \Laravel\Scout\Builder
*/
public static function scoutQuery(NovaRequest $request, $query)
{
return $query;
}
/**
* Build a "detail" query for the given resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public static function detailQuery(NovaRequest $request, $query)
{
return parent::detailQuery($request, $query);
}
/**
* Build a "relatable" query for the given resource.
*
* This query determines which instances of the model may be attached to other resources.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public static function relatableQuery(NovaRequest $request, $query)
{
return parent::relatableQuery($request, $query);
}
}

92
app/Nova/Sentence.php Normal file
View File

@@ -0,0 +1,92 @@
<?php
namespace App\Nova;
use Illuminate\Http\Request;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Http\Requests\NovaRequest;
class Sentence extends Resource
{
/**
* The model the resource corresponds to.
*
* @var class-string<\App\Models\Sentence>
*/
public static $model = \App\Models\Sentence::class;
/**
* The single value that should be used to represent the resource when being displayed.
*
* @var string
*/
public static $title = 'name';
/**
* The columns that should be searched.
*
* @var array
*/
public static $search = [
'name',
];
/**
* Get the fields displayed by the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [
ID::make()->sortable(),
Text::make('Name'),
];
}
/**
* Get the cards available for the request.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function cards(NovaRequest $request)
{
return [];
}
/**
* Get the filters available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function filters(NovaRequest $request)
{
return [];
}
/**
* Get the lenses available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function lenses(NovaRequest $request)
{
return [];
}
/**
* Get the actions available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function actions(NovaRequest $request)
{
return [];
}
}

111
app/Nova/User.php Normal file
View File

@@ -0,0 +1,111 @@
<?php
namespace App\Nova;
use Illuminate\Http\Request;
use Illuminate\Validation\Rules;
use Laravel\Nova\Fields\Gravatar;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Password;
use Laravel\Nova\Fields\Text;
use Laravel\Nova\Http\Requests\NovaRequest;
class User extends Resource
{
/**
* The model the resource corresponds to.
*
* @var class-string<\App\Models\User>
*/
public static $model = \App\Models\User::class;
/**
* The single value that should be used to represent the resource when being displayed.
*
* @var string
*/
public static $title = 'name';
/**
* The columns that should be searched.
*
* @var array
*/
public static $search = [
'id', 'name', 'email',
];
/**
* Get the fields displayed by the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
{
return [
ID::make()->sortable(),
Gravatar::make()->maxWidth(50),
Text::make('Name')
->sortable()
->rules('required', 'max:255'),
Text::make('Email')
->sortable()
->rules('required', 'email', 'max:254')
->creationRules('unique:users,email')
->updateRules('unique:users,email,{{resourceId}}'),
Password::make('Password')
->onlyOnForms()
->creationRules('required', Rules\Password::defaults())
->updateRules('nullable', Rules\Password::defaults()),
];
}
/**
* Get the cards available for the request.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function cards(NovaRequest $request)
{
return [];
}
/**
* Get the filters available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function filters(NovaRequest $request)
{
return [];
}
/**
* Get the lenses available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function lenses(NovaRequest $request)
{
return [];
}
/**
* Get the actions available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function actions(NovaRequest $request)
{
return [];
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Laravel\Nova\Nova;
use Laravel\Nova\NovaApplicationServiceProvider;
class NovaServiceProvider extends NovaApplicationServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
parent::boot();
}
/**
* Register the Nova routes.
*
* @return void
*/
protected function routes()
{
Nova::routes()
->withAuthenticationRoutes()
->withPasswordResetRoutes()
->register();
}
/**
* Register the Nova gate.
*
* This gate determines who can access Nova in non-local environments.
*
* @return void
*/
protected function gate()
{
Gate::define('viewNova', function ($user) {
return in_array($user->email, [
'hugo.lafay@gmail.com'
]);
});
}
/**
* Get the dashboards that should be listed in the Nova sidebar.
*
* @return array
*/
protected function dashboards()
{
return [
new \App\Nova\Dashboards\Main,
];
}
/**
* Get the tools that should be listed in the Nova sidebar.
*
* @return array
*/
public function tools()
{
return [];
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
}