зеркало из
https://github.com/M82-project/DIMA.git
synced 2025-10-29 05:04:18 +02:00
Ajout Storm1516 et détection des handles réseaux sociaux
Этот коммит содержится в:
родитель
07f11cc250
Коммит
a715246f0b
6124
plugin/plugin_chrome/releases/Plugin-dima/data/databases/Storm1516.js
Обычный файл
6124
plugin/plugin_chrome/releases/Plugin-dima/data/databases/Storm1516.js
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
@ -20,6 +20,7 @@
|
|||||||
"data/databases/PortalKombat.js",
|
"data/databases/PortalKombat.js",
|
||||||
"data/databases/RRN.js",
|
"data/databases/RRN.js",
|
||||||
"data/databases/Baybridge.js",
|
"data/databases/Baybridge.js",
|
||||||
|
"data/databases/Storm1516.js",
|
||||||
"data/techniques.js",
|
"data/techniques.js",
|
||||||
"data/keywords.js",
|
"data/keywords.js",
|
||||||
"modules/contentExtractor.js",
|
"modules/contentExtractor.js",
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
// DIMA - Gestionnaire Central de Sites Suspects
|
// DIMA - Gestionnaire Central de Sites Suspects
|
||||||
|
// Version 2.2 - Support COMPLET des comptes sociaux (format Storm1516 natif)
|
||||||
// Ce fichier charge et agrège toutes les bases de données de domaines suspects
|
// Ce fichier charge et agrège toutes les bases de données de domaines suspects
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gestionnaire centralisé des sites suspects
|
* Gestionnaire centralisé des sites suspects
|
||||||
* Charge automatiquement toutes les bases de données disponibles
|
* Compatible avec TOUS les formats de données existants
|
||||||
* et fournit une API unifiée pour vérifier les sites
|
|
||||||
*/
|
*/
|
||||||
class SuspiciousSitesManager {
|
class SuspiciousSitesManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -12,9 +12,12 @@ class SuspiciousSitesManager {
|
|||||||
this.allSites = [];
|
this.allSites = [];
|
||||||
this.stats = {
|
this.stats = {
|
||||||
totalSites: 0,
|
totalSites: 0,
|
||||||
|
totalDomains: 0,
|
||||||
|
totalSocialAccounts: 0,
|
||||||
byRiskLevel: { high: 0, medium: 0, low: 0 },
|
byRiskLevel: { high: 0, medium: 0, low: 0 },
|
||||||
bySources: {},
|
bySources: {},
|
||||||
byTags: {}
|
byTags: {},
|
||||||
|
bySocialPlatform: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
@ -35,7 +38,9 @@ class SuspiciousSitesManager {
|
|||||||
// Calculer les statistiques
|
// Calculer les statistiques
|
||||||
this.calculateStats();
|
this.calculateStats();
|
||||||
|
|
||||||
console.log(`✅ DIMA: ${this.allSites.length} sites suspects chargés depuis ${this.sources.size} source(s)`);
|
console.log(`✅ DIMA: ${this.allSites.length} entrées chargées depuis ${this.sources.size} source(s)`);
|
||||||
|
console.log(` - ${this.stats.totalDomains} domaines`);
|
||||||
|
console.log(` - ${this.stats.totalSocialAccounts} comptes de réseaux sociaux`);
|
||||||
this.logStats();
|
this.logStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +95,31 @@ class SuspiciousSitesManager {
|
|||||||
});
|
});
|
||||||
console.log(` ✓ Source Baybridge chargée: ${baybridgeDomains.length} domaines`);
|
console.log(` ✓ Source Baybridge chargée: ${baybridgeDomains.length} domaines`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Source 5: Storm 1516 - Domaines (VIGINUM)
|
||||||
|
if (typeof storm1516Domains !== 'undefined' && Array.isArray(storm1516Domains)) {
|
||||||
|
this.registerSource('Storm1516_Domains', storm1516Domains, {
|
||||||
|
name: 'Opération Storm_1516 (Domaines)',
|
||||||
|
description: 'Mode opératoire informationnel (MOI) russe actif depuis août 2023',
|
||||||
|
organization: 'VIGINUM',
|
||||||
|
reportUrl: 'https://www.defense.gouv.fr/sites/default/files/desinformation/Rapport%20Storm%201516%20-%20SGDSN.pdf',
|
||||||
|
reportDate: '2025-05-02'
|
||||||
|
});
|
||||||
|
console.log(` ✓ Source Storm 1516 (domaines) chargée: ${storm1516Domains.length} domaines`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Source 6: Storm 1516 - Comptes sociaux (VIGINUM) - FORMAT NATIF
|
||||||
|
if (typeof storm1516SocialAccounts !== 'undefined' && Array.isArray(storm1516SocialAccounts)) {
|
||||||
|
this.registerSource('Storm1516_Social', storm1516SocialAccounts, {
|
||||||
|
name: 'Opération Storm_1516 (Comptes sociaux)',
|
||||||
|
description: 'Comptes de réseaux sociaux relayant le MOI russe Storm 1516',
|
||||||
|
organization: 'VIGINUM',
|
||||||
|
reportUrl: 'https://www.defense.gouv.fr/sites/default/files/desinformation/Rapport%20Storm%201516%20-%20SGDSN.pdf',
|
||||||
|
reportDate: '2025-05-02'
|
||||||
|
});
|
||||||
|
console.log(` ✓ Source Storm 1516 (comptes sociaux) chargée: ${storm1516SocialAccounts.length} comptes`);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Avertissement si aucune source n'est chargée
|
// Avertissement si aucune source n'est chargée
|
||||||
if (this.sources.size === 0) {
|
if (this.sources.size === 0) {
|
||||||
@ -125,14 +155,28 @@ class SuspiciousSitesManager {
|
|||||||
*/
|
*/
|
||||||
calculateStats() {
|
calculateStats() {
|
||||||
this.stats.totalSites = this.allSites.length;
|
this.stats.totalSites = this.allSites.length;
|
||||||
|
this.stats.totalDomains = 0;
|
||||||
|
this.stats.totalSocialAccounts = 0;
|
||||||
|
|
||||||
// Reset stats
|
// Reset stats
|
||||||
this.stats.byRiskLevel = { high: 0, medium: 0, low: 0 };
|
this.stats.byRiskLevel = { high: 0, medium: 0, low: 0 };
|
||||||
this.stats.bySources = {};
|
this.stats.bySources = {};
|
||||||
this.stats.byTags = {};
|
this.stats.byTags = {};
|
||||||
|
this.stats.bySocialPlatform = {};
|
||||||
|
|
||||||
// Compter par niveau de risque et tags
|
// Compter par niveau de risque et tags
|
||||||
this.allSites.forEach(site => {
|
this.allSites.forEach(site => {
|
||||||
|
// Distinguer domaines et comptes sociaux
|
||||||
|
// Format Storm1516: {platform: "X/Twitter", handle: "@..."}
|
||||||
|
// Format standard: {domain: "...", accountType: "twitter"}
|
||||||
|
if (site.platform || site.accountType) {
|
||||||
|
this.stats.totalSocialAccounts++;
|
||||||
|
const platform = site.platform || site.accountType;
|
||||||
|
this.stats.bySocialPlatform[platform] = (this.stats.bySocialPlatform[platform] || 0) + 1;
|
||||||
|
} else {
|
||||||
|
this.stats.totalDomains++;
|
||||||
|
}
|
||||||
|
|
||||||
// Par niveau de risque
|
// Par niveau de risque
|
||||||
if (site.riskLevel) {
|
if (site.riskLevel) {
|
||||||
this.stats.byRiskLevel[site.riskLevel] = (this.stats.byRiskLevel[site.riskLevel] || 0) + 1;
|
this.stats.byRiskLevel[site.riskLevel] = (this.stats.byRiskLevel[site.riskLevel] || 0) + 1;
|
||||||
@ -157,7 +201,15 @@ class SuspiciousSitesManager {
|
|||||||
*/
|
*/
|
||||||
logStats() {
|
logStats() {
|
||||||
console.log('📊 Statistiques:');
|
console.log('📊 Statistiques:');
|
||||||
console.log(` Total: ${this.stats.totalSites} sites`);
|
console.log(` Total: ${this.stats.totalSites} entrées`);
|
||||||
|
console.log(` - Domaines: ${this.stats.totalDomains}`);
|
||||||
|
console.log(` - Comptes sociaux: ${this.stats.totalSocialAccounts}`);
|
||||||
|
if (this.stats.totalSocialAccounts > 0) {
|
||||||
|
console.log(' Répartition par plateforme:');
|
||||||
|
for (const [platform, count] of Object.entries(this.stats.bySocialPlatform)) {
|
||||||
|
console.log(` • ${platform}: ${count}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
console.log(` Risque élevé: ${this.stats.byRiskLevel.high || 0}`);
|
console.log(` Risque élevé: ${this.stats.byRiskLevel.high || 0}`);
|
||||||
console.log(` Risque moyen: ${this.stats.byRiskLevel.medium || 0}`);
|
console.log(` Risque moyen: ${this.stats.byRiskLevel.medium || 0}`);
|
||||||
console.log(` Risque faible: ${this.stats.byRiskLevel.low || 0}`);
|
console.log(` Risque faible: ${this.stats.byRiskLevel.low || 0}`);
|
||||||
@ -165,7 +217,7 @@ class SuspiciousSitesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vérifie si une URL correspond à un site suspect
|
* Vérifie si une URL correspond à un site suspect OU un compte social suspect
|
||||||
* @param {string} url - L'URL à vérifier
|
* @param {string} url - L'URL à vérifier
|
||||||
* @returns {Object} Résultat de la vérification
|
* @returns {Object} Résultat de la vérification
|
||||||
*/
|
*/
|
||||||
@ -173,36 +225,64 @@ class SuspiciousSitesManager {
|
|||||||
try {
|
try {
|
||||||
const urlObj = new URL(url);
|
const urlObj = new URL(url);
|
||||||
const hostname = urlObj.hostname.toLowerCase();
|
const hostname = urlObj.hostname.toLowerCase();
|
||||||
|
const pathname = urlObj.pathname.toLowerCase();
|
||||||
|
|
||||||
for (const site of this.allSites) {
|
for (const site of this.allSites) {
|
||||||
let isMatch = false;
|
let isMatch = false;
|
||||||
|
let matchType = 'domain';
|
||||||
|
|
||||||
switch (site.matchType) {
|
// NOUVEAU: Support du format Storm1516 natif
|
||||||
case "exact":
|
// Format: {platform: "X/Twitter", handle: "@JimFergusonUK", url: "..."}
|
||||||
isMatch = hostname === site.domain.toLowerCase() ||
|
if (site.platform && site.handle) {
|
||||||
hostname === `www.${site.domain.toLowerCase()}`;
|
isMatch = this.checkSocialAccountStorm1516Format(url, site, hostname, pathname);
|
||||||
break;
|
matchType = 'social_account';
|
||||||
|
}
|
||||||
case "contains":
|
// Support du format standard avec accountType
|
||||||
isMatch = hostname.includes(site.domain.toLowerCase());
|
else if (site.accountType) {
|
||||||
break;
|
const extractedHandle = this.extractSocialHandle(url, site.accountType);
|
||||||
|
if (extractedHandle) {
|
||||||
case "pattern":
|
const dbHandle = site.domain.toLowerCase().replace(/^@/, '');
|
||||||
try {
|
isMatch = extractedHandle === dbHandle;
|
||||||
const regex = new RegExp(site.domain, "i");
|
matchType = 'social_account';
|
||||||
isMatch = regex.test(hostname);
|
}
|
||||||
} catch (e) {
|
}
|
||||||
console.error(`DIMA: Pattern regex invalide pour ${site.domain}:`, e);
|
// Vérification classique pour les domaines
|
||||||
}
|
else {
|
||||||
break;
|
switch (site.matchType) {
|
||||||
|
case "exact":
|
||||||
|
isMatch = hostname === site.domain.toLowerCase() ||
|
||||||
|
hostname === `www.${site.domain.toLowerCase()}`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "contains":
|
||||||
|
isMatch = hostname.includes(site.domain.toLowerCase());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "pattern":
|
||||||
|
try {
|
||||||
|
const regex = new RegExp(site.domain, "i");
|
||||||
|
isMatch = regex.test(hostname);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`DIMA: Pattern regex invalide pour ${site.domain}:`, e);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMatch) {
|
if (isMatch) {
|
||||||
|
console.log(`🎯 DIMA: Match trouvé!`, {
|
||||||
|
type: matchType,
|
||||||
|
site: site.handle || site.domain,
|
||||||
|
url: url
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isSuspicious: true,
|
isSuspicious: true,
|
||||||
siteInfo: site,
|
siteInfo: site,
|
||||||
riskConfig: this.getRiskConfig(site.riskLevel),
|
riskConfig: this.getRiskConfig(site.riskLevel),
|
||||||
matchedHostname: hostname
|
matchedHostname: hostname,
|
||||||
|
matchType: matchType,
|
||||||
|
matchedIdentifier: matchType === 'social_account' ? (site.handle || site.domain) : hostname
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,28 +294,168 @@ class SuspiciousSitesManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOUVEAU: Vérifie un compte social au format Storm1516
|
||||||
|
* Format: {platform: "X/Twitter", handle: "@JimFergusonUK"}
|
||||||
|
*/
|
||||||
|
checkSocialAccountStorm1516Format(url, site, hostname, pathname) {
|
||||||
|
// Mapping des plateformes Storm1516 vers domaines
|
||||||
|
const platformDomains = {
|
||||||
|
'X/Twitter': ['twitter.com', 'x.com'],
|
||||||
|
'Telegram': ['t.me', 'telegram.me'],
|
||||||
|
'YouTube': ['youtube.com', 'youtu.be'],
|
||||||
|
'Facebook': ['facebook.com', 'fb.com', 'm.facebook.com'],
|
||||||
|
'Instagram': ['instagram.com'],
|
||||||
|
'TikTok': ['tiktok.com'],
|
||||||
|
'VK': ['vk.com'],
|
||||||
|
'Rumble': ['rumble.com']
|
||||||
|
};
|
||||||
|
|
||||||
|
const platform = site.platform;
|
||||||
|
const handle = site.handle.toLowerCase().replace(/^@/, ''); // Enlever @ et lowercase
|
||||||
|
|
||||||
|
// Vérifier si on est sur la bonne plateforme
|
||||||
|
const domains = platformDomains[platform];
|
||||||
|
if (!domains) {
|
||||||
|
console.warn(`DIMA: Plateforme inconnue: ${platform}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCorrectDomain = domains.some(domain => hostname.includes(domain));
|
||||||
|
if (!isCorrectDomain) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extraire le handle de l'URL actuelle
|
||||||
|
let extractedHandle = null;
|
||||||
|
|
||||||
|
if (platform === 'X/Twitter') {
|
||||||
|
// twitter.com/JimFergusonUK ou x.com/JimFergusonUK
|
||||||
|
const match = pathname.match(/^\/([a-zA-Z0-9_]+)(?:\/|$|\?)/);
|
||||||
|
if (match) extractedHandle = match[1].toLowerCase();
|
||||||
|
} else if (platform === 'Telegram') {
|
||||||
|
// t.me/username ou t.me/s/channelname
|
||||||
|
const match = pathname.match(/^\/(?:s\/)?([a-zA-Z0-9_]+)(?:\/|$|\?)/);
|
||||||
|
if (match) extractedHandle = match[1].toLowerCase();
|
||||||
|
} else if (platform === 'YouTube') {
|
||||||
|
// youtube.com/@username ou youtube.com/c/username
|
||||||
|
const match = pathname.match(/^\/([@c]\/)?([a-zA-Z0-9_-]+)(?:\/|$|\?)/);
|
||||||
|
if (match) extractedHandle = match[2].toLowerCase();
|
||||||
|
} else if (platform === 'Facebook') {
|
||||||
|
// facebook.com/username
|
||||||
|
const match = pathname.match(/^\/([a-zA-Z0-9._-]+)(?:\/|$|\?)/);
|
||||||
|
if (match) extractedHandle = match[1].toLowerCase();
|
||||||
|
} else if (platform === 'Rumble') {
|
||||||
|
// rumble.com/c/username
|
||||||
|
const match = pathname.match(/^\/c\/([a-zA-Z0-9_-]+)(?:\/|$|\?)/);
|
||||||
|
if (match) extractedHandle = match[1].toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extractedHandle) {
|
||||||
|
console.log(`🔍 DIMA: Comparaison - URL: "${extractedHandle}" vs DB: "${handle}"`);
|
||||||
|
return extractedHandle === handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extrait le handle/username d'une URL de réseau social
|
||||||
|
* @param {string} url - L'URL complète
|
||||||
|
* @param {string} accountType - Type de compte (twitter, facebook, youtube, etc.)
|
||||||
|
* @returns {string|null} Le handle extrait ou null
|
||||||
|
*/
|
||||||
|
extractSocialHandle(url, accountType) {
|
||||||
|
try {
|
||||||
|
const urlObj = new URL(url);
|
||||||
|
const hostname = urlObj.hostname.toLowerCase();
|
||||||
|
const pathname = urlObj.pathname;
|
||||||
|
|
||||||
|
// Patterns pour différents réseaux sociaux
|
||||||
|
const patterns = {
|
||||||
|
twitter: {
|
||||||
|
domains: ['twitter.com', 'x.com'],
|
||||||
|
regex: /^\/([a-zA-Z0-9_]+)(?:\/|$|\?)/
|
||||||
|
},
|
||||||
|
facebook: {
|
||||||
|
domains: ['facebook.com', 'fb.com'],
|
||||||
|
regex: /^\/([a-zA-Z0-9._]+)(?:\/|$|\?)/
|
||||||
|
},
|
||||||
|
instagram: {
|
||||||
|
domains: ['instagram.com'],
|
||||||
|
regex: /^\/([a-zA-Z0-9._]+)(?:\/|$|\?)/
|
||||||
|
},
|
||||||
|
youtube: {
|
||||||
|
domains: ['youtube.com'],
|
||||||
|
regex: /^\/([@c]\/)?([a-zA-Z0-9_-]+)(?:\/|$|\?)/
|
||||||
|
},
|
||||||
|
telegram: {
|
||||||
|
domains: ['t.me', 'telegram.me'],
|
||||||
|
regex: /^\/([a-zA-Z0-9_]+)(?:\/|$|\?)/
|
||||||
|
},
|
||||||
|
tiktok: {
|
||||||
|
domains: ['tiktok.com'],
|
||||||
|
regex: /^\/@?([a-zA-Z0-9._]+)(?:\/|$|\?)/
|
||||||
|
},
|
||||||
|
vk: {
|
||||||
|
domains: ['vk.com'],
|
||||||
|
regex: /^\/([a-zA-Z0-9._]+)(?:\/|$|\?)/
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const pattern = patterns[accountType.toLowerCase()];
|
||||||
|
if (!pattern) {
|
||||||
|
console.warn(`DIMA: Type de compte non supporté: ${accountType}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier si on est sur le bon domaine
|
||||||
|
const isCorrectDomain = pattern.domains.some(domain => hostname.includes(domain));
|
||||||
|
if (!isCorrectDomain) return null;
|
||||||
|
|
||||||
|
// Extraire le handle
|
||||||
|
const match = pathname.match(pattern.regex);
|
||||||
|
if (match) {
|
||||||
|
const handle = accountType.toLowerCase() === 'youtube' ? (match[2] || match[1]) : match[1];
|
||||||
|
console.log(`DIMA: Handle extrait de ${accountType}: ${handle}`);
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("DIMA: Erreur lors de l'extraction du handle social:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retourne la configuration visuelle pour un niveau de risque
|
* Retourne la configuration visuelle pour un niveau de risque
|
||||||
*/
|
*/
|
||||||
getRiskConfig(riskLevel) {
|
getRiskConfig(riskLevel) {
|
||||||
const RISK_LEVELS = {
|
const RISK_LEVELS = {
|
||||||
|
critical: {
|
||||||
|
color: "#8b0000",
|
||||||
|
icon: "🚨",
|
||||||
|
label: "Risque Critique",
|
||||||
|
message: "Ce site/compte a été identifié comme un acteur majeur de désinformation."
|
||||||
|
},
|
||||||
high: {
|
high: {
|
||||||
color: "#c0392b",
|
color: "#c0392b",
|
||||||
icon: "⚠️",
|
icon: "⚠️",
|
||||||
label: "Risque Élevé",
|
label: "Risque Élevé",
|
||||||
message: "Ce site a été identifié comme diffusant de la désinformation de manière systématique."
|
message: "Ce site/compte a été identifié comme diffusant de la désinformation de manière systématique."
|
||||||
},
|
},
|
||||||
medium: {
|
medium: {
|
||||||
color: "#e67e22",
|
color: "#e67e22",
|
||||||
icon: "⚡",
|
icon: "⚡",
|
||||||
label: "Vigilance Requise",
|
label: "Vigilance Requise",
|
||||||
message: "Ce site a été signalé pour des pratiques douteuses."
|
message: "Ce site/compte a été signalé pour des pratiques douteuses."
|
||||||
},
|
},
|
||||||
low: {
|
low: {
|
||||||
color: "#f39c12",
|
color: "#f39c12",
|
||||||
icon: "ℹ️",
|
icon: "ℹ️",
|
||||||
label: "À Surveiller",
|
label: "À Surveiller",
|
||||||
message: "Ce site présente des caractéristiques suspectes."
|
message: "Ce site/compte présente des caractéristiques suspectes."
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -281,6 +501,16 @@ class SuspiciousSitesManager {
|
|||||||
site.source === sourceName
|
site.source === sourceName
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recherche des comptes sociaux par plateforme
|
||||||
|
*/
|
||||||
|
searchBySocialPlatform(platform) {
|
||||||
|
return this.allSites.filter(site =>
|
||||||
|
site.platform === platform ||
|
||||||
|
(site.accountType && site.accountType.toLowerCase() === platform.toLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialisation automatique du gestionnaire
|
// Initialisation automatique du gestionnaire
|
||||||
|
|||||||
Загрузка…
x
Ссылка в новой задаче
Block a user