Java-приложение для анализа Social Dating Rank в Telegram.

Автор: Сергей Владимирович

Java-приложение для анализа Social Dating Rank в Telegram. Будем использовать Telegram API (Telegram Bot API) и библиотеку JGraphT для реализации PageRank. Полный проект: Telegram Social Dating Rank Analyzer 1. Структура проекта ` telegram-social-dating/ ├── pom.xml ├── src/main/java/com/socialdating/ │ ├── Main.java │ ├── bot/ │ │ ├── TelegramBot.java │ │ └── BotCommands.java │ ├── analyzer/ │ │ ├── SocialGraphBuilder.java │ │ ├── DatingRankCalculator.java │ │ └── InteractionWeights.java │ ├── model/ │ │ ├── User.java │ │ ├── Interaction.java │ │ └── InteractionType.java │ ├── storage/ │ │ ├── GraphStorage.java │ │ └── UserRepository.java │ └── api/ │ └── TelegramClient.java └── config/ └── application.properties ` 2. Файл зависимостей (pom.xml) `xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.socialdating</groupId> <artifactId>telegram-social-dating</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- Telegram Bot API --> <dependency> <groupId>org.telegram</groupId> <artifactId>telegrambots</artifactId> <version>6.8.0</version> </dependency> <!-- Graph Algorithms --> <dependency> <groupId>org.jgrapht</groupId> <artifactId>jgrapht-core</artifactId> <version>1.5.2</version> </dependency> <!-- Database --> <dependency> <groupId>org.xerial</groupId> <artifactId>sqlite-jdbc</artifactId> <version>3.42.0.0</version> </dependency> <!-- JSON Processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency> <!-- Configuration --> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>2.0</version> </dependency> <!-- Logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>2.0.7</version> </dependency> <!-- HTTP Client --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.14</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.4.1</version> <executions> <execution> <phase>package</phase> <goals><goal>shade</goal></goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.socialdating.Main</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build> </project> ` 3. Модели данных User.java: `java package com.socialdating.model; import java.time.LocalDateTime; import java.util.HashSet; import java.util.Set; public class User { private Long id; private String username; private String firstName; private String lastName; private Integer followerCount; private LocalDateTime joinedDate; private Double datingRank; private Set<Interaction> interactions = new HashSet<>(); // Конструкторы, геттеры, сеттеры public User() {} public User(Long id, String username, String firstName) { this.id = id; this.username = username; this.firstName = firstName; this.datingRank = 1.0; // Базовый рейтинг this.joinedDate = LocalDateTime.now(); } public void addInteraction(Interaction interaction) { this.interactions.add(interaction); } public double getTotalOutgoingWeight() { return interactions.stream() .filter(i -> i.getFromUser().equals(this)) .mapToDouble(Interaction::getWeight) .sum(); } // Getters and Setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Integer getFollowerCount() { return followerCount; } public void setFollowerCount(Integer followerCount) { this.followerCount = followerCount; } public LocalDateTime getJoinedDate() { return joinedDate; } public void setJoinedDate(LocalDateTime joinedDate) { this.joinedDate = joinedDate; } public Double getDatingRank() { return datingRank; } public void setDatingRank(Double datingRank) { this.datingRank = datingRank; } public Set<Interaction> getInteractions() { return interactions; } public void setInteractions(Set<Interaction> interactions) { this.interactions = interactions; } } ` Interaction.java: `java package com.socialdating.model; import java.time.LocalDateTime; public class Interaction { private Long id; private User fromUser; private User toUser; private InteractionType type; private Double weight; private LocalDateTime timestamp; private String contentHash; // Для уникальности public Interaction() {} public Interaction(User fromUser, User toUser, InteractionType type) { this.fromUser = fromUser; this.toUser = toUser; this.type = type; this.weight = type.getBaseWeight(); this.timestamp = LocalDateTime.now(); this.contentHash = generateHash(); } private String generateHash() { return fromUser.getId() + "" + toUser.getId() + "" + type.name() + "_" + timestamp.toEpochSecond(); } // Getters and Setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public User getFromUser() { return fromUser; } public void setFromUser(User fromUser) { this.fromUser = fromUser; } public User getToUser() { return toUser; } public void setToUser(User toUser) { this.toUser = toUser; } public InteractionType getType() { return type; } public void setType(InteractionType type) { this.type = type; this.weight = type.getBaseWeight(); } public Double getWeight() { return weight; } public void setWeight(Double weight) { this.weight = weight; } public LocalDateTime getTimestamp() { return timestamp; } public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; } public String getContentHash() { return contentHash; } } ` InteractionType.java: `java package com.socialdating.model; public enum InteractionType { // Веса для разных типов взаимодействий MENTION(0.8, "Упоминание @username"), REPLY(0.7, "Ответ на сообщение"), FORWARD(0.9, "Пересылка сообщения"), REACTION(0.3, "Реакция (лайк, дизлайк)"), REPLYTOSTORY(0.6, "Ответ на историю"), QUOTE(0.85, "Цитирование сообщения"), PIN(0.5, "Закрепление сообщения"), SAVE_MESSAGE(0.4, "Сохранение сообщения"), JOINVIALINK(0.2, "Вступление по ссылке пользователя"), DONATION(1.5, "Финансовый донат"); private final double baseWeight; private final String description; InteractionType(double baseWeight, String description) { this.baseWeight = baseWeight; this.description = description; } public double getBaseWeight() { return baseWeight; } public String getDescription() { return description; } public static InteractionType fromString(String text) { for (InteractionType type : InteractionType.values()) { if (type.name().equalsIgnoreCase(text)) { return type; } } return REACTION; // По умолчанию } } ` 4. Построение социального графа SocialGraphBuilder.java: `java package com.socialdating.analyzer; import com.socialdating.model.User; import com.socialdating.model.Interaction; import org.jgrapht.Graph; import org.jgrapht.graph.DefaultDirectedWeightedGraph; import org.jgrapht.graph.DefaultWeightedEdge; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.*; public class SocialGraphBuilder { private final Map<Long, User> users = new HashMap<>(); private final List<Interaction> interactions = new ArrayList<>(); private final double timeDecayFactor; // Фактор временного затухания public SocialGraphBuilder(double timeDecayFactor) { this.timeDecayFactor = timeDecayFactor; } public void addUser(User user) { users.put(user.getId(), user); } public void addInteraction(Interaction interaction) { interactions.add(interaction); // Добавляем пользователей, если их еще нет if (!users.containsKey(interaction.getFromUser().getId())) { users.put(interaction.getFromUser().getId(), interaction.getFromUser()); } if (!users.containsKey(interaction.getToUser().getId())) { users.put(interaction.getToUser().getId(), interaction.getToUser()); } } public Graph<User, DefaultWeightedEdge> buildGraph(LocalDateTime analysisDate) { Graph<User, DefaultWeightedEdge> graph = new DefaultDirectedWeightedGraph<>(DefaultWeightedEdge.class); // Добавляем вершины (пользователей) for (User user : users.values()) { graph.addVertex(user); } // Добавляем рёбра (взаимодействия) с учётом времени for (Interaction interaction : interactions) { User from = users.get(interaction.getFromUser().getId()); User to = users.get(interaction.getToUser().getId()); if (from != null && to != null && !from.equals(to)) { double timeWeight = calculateTimeDecay(interaction.getTimestamp(), analysisDate); double finalWeight = interaction.getWeight() * timeWeight; if (finalWeight > 0.01) { // Игнорируем слишком старые взаимодействия DefaultWeightedEdge edge = graph.getEdge(from, to); if (edge == null) { edge = graph.addEdge(from, to); graph.setEdgeWeight(edge, finalWeight); } else { // Агрегируем веса для повторных взаимодействий double currentWeight = graph.getEdgeWeight(edge); graph.setEdgeWeight(edge, currentWeight + finalWeight); } } } } return graph; } private double calculateTimeDecay(LocalDateTime eventTime, LocalDateTime currentTime) { long daysBetween = ChronoUnit.DAYS.between(eventTime, currentTime); // Экспоненциальное затухание: вес = e^(-λ * days) // где λ = timeDecayFactor (например, 0.01 = 1% затухания в день) return Math.exp(-timeDecayFactor * daysBetween); } public Map<Long, User> getUsers() { return Collections.unmodifiableMap(users); } public List<Interaction> getInteractions() { return Collections.unmodifiableList(interactions); } } ` 5. Калькулятор Social Dating Rank DatingRankCalculator.java: `java package com.socialdating.analyzer; import com.socialdating.model.User; import org.jgrapht.Graph; import org.jgrapht.graph.DefaultWeightedEdge; import java.util.HashMap; import java.util.Map; public class DatingRankCalculator { private static final double DAMPING_FACTOR = 0.85; private static final double CONVERGENCE_THRESHOLD = 0.0001; private static final int MAX_ITERATIONS = 100; public Map<User, Double> calculatePageRank(Graph<User, DefaultWeightedEdge> graph) { int vertexCount = graph.vertexSet().size(); if (vertexCount == 0) { return new HashMap<>(); } // Инициализация Map<User, Double> currentRank = new HashMap<>(); double initialRank = 1.0 / vertexCount; for (User user : graph.vertexSet()) { currentRank.put(user, initialRank); } // Итеративный расчёт PageRank for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++) { Map<User, Double> nextRank = new HashMap<>(); // Базовая часть (телепортация) double teleportation = (1.0 - DAMPING_FACTOR) / vertexCount; for (User user : graph.vertexSet()) { nextRank.put(user, teleportation); } // Распространение ранга по рёбрам for (User source : graph.vertexSet()) { double sourceRank = currentRank.get(source); double totalOutgoingWeight = 0.0; // Считаем общий вес исходящих рёбер for (DefaultWeightedEdge edge : graph.outgoingEdgesOf(source)) {

Java-приложение для анализа Social Dating Rank в Telegram.

NoteСергей Владимирович17.01.2026, 17:53:51
Открыть в эксплорере

SeqNo

2997891-1

Тип

Note

Комиссия

0.043467

Размер

43367 B

Создатель

77ueq3kNSzpPGUAaerpMhC6DZbUKT55FMa

Подпись

5oW9N89X9QEijn4hTVEaGniaZctaQ99qTJ1CT9neP3xqU5Z9g3jDendfJdWgX1HpZc7X86BH3Ssa6yVQZvkyzPLY

Содержание

Java-приложение для анализа Social Dating Rank в Telegram.

Сергей Владимирович

Java-приложение для анализа Social Dating Rank в Telegram. Будем использовать Telegram API (Telegram Bot API) и библиотеку JGraphT для реализации PageRank.

Полный проект: Telegram Social Dating Rank Analyzer

1. Структура проекта

telegram-social-dating/
├── pom.xml
├── src/main/java/com/socialdating/
│   ├── Main.java
│   ├── bot/
│   │   ├── TelegramBot.java
│   │   └── BotCommands.java
│   ├── analyzer/
│   │   ├── SocialGraphBuilder.java
│   │   ├── DatingRankCalculator.java
│   │   └── InteractionWeights.java
│   ├── model/
│   │   ├── User.java
│   │   ├── Interaction.java
│   │   └── InteractionType.java
│   ├── storage/
│   │   ├── GraphStorage.java
│   │   └── UserRepository.java
│   └── api/
│       └── TelegramClient.java
└── config/
    └── application.properties

2. Файл зависимостей (pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.socialdating</groupId>
    <artifactId>telegram-social-dating</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- Telegram Bot API -->
        <dependency>
            <groupId>org.telegram</groupId>
            <artifactId>telegrambots</artifactId>
            <version>6.8.0</version>
        </dependency>
        
        <!-- Graph Algorithms -->
        <dependency>
            <groupId>org.jgrapht</groupId>
            <artifactId>jgrapht-core</artifactId>
            <version>1.5.2</version>
        </dependency>
        
        <!-- Database -->
        <dependency>
            <groupId>org.xerial</groupId>
            <artifactId>sqlite-jdbc</artifactId>
            <version>3.42.0.0</version>
        </dependency>
        
        <!-- JSON Processing -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.15.2</version>
        </dependency>
        
        <!-- Configuration -->
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>2.0</version>
        </dependency>
        
        <!-- Logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.7</version>
        </dependency>
        
        <!-- HTTP Client -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.14</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.4.1</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals><goal>shade</goal></goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.socialdating.Main</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

3. Модели данных

User.java:

package com.socialdating.model;

import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;

public class User {
    private Long id;
    private String username;
    private String firstName;
    private String lastName;
    private Integer followerCount;
    private LocalDateTime joinedDate;
    private Double datingRank;
    private Set<Interaction> interactions = new HashSet<>();
    
    // Конструкторы, геттеры, сеттеры
    public User() {}
    
    public User(Long id, String username, String firstName) {
        this.id = id;
        this.username = username;
        this.firstName = firstName;
        this.datingRank = 1.0; // Базовый рейтинг
        this.joinedDate = LocalDateTime.now();
    }
    
    public void addInteraction(Interaction interaction) {
        this.interactions.add(interaction);
    }
    
    public double getTotalOutgoingWeight() {
        return interactions.stream()
            .filter(i -> i.getFromUser().equals(this))
            .mapToDouble(Interaction::getWeight)
            .sum();
    }
    
    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    
    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    
    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    
    public Integer getFollowerCount() { return followerCount; }
    public void setFollowerCount(Integer followerCount) { this.followerCount = followerCount; }
    
    public LocalDateTime getJoinedDate() { return joinedDate; }
    public void setJoinedDate(LocalDateTime joinedDate) { this.joinedDate = joinedDate; }
    
    public Double getDatingRank() { return datingRank; }
    public void setDatingRank(Double datingRank) { this.datingRank = datingRank; }
    
    public Set<Interaction> getInteractions() { return interactions; }
    public void setInteractions(Set<Interaction> interactions) { this.interactions = interactions; }
}

Interaction.java:

package com.socialdating.model;

import java.time.LocalDateTime;

public class Interaction {
    private Long id;
    private User fromUser;
    private User toUser;
    private InteractionType type;
    private Double weight;
    private LocalDateTime timestamp;
    private String contentHash; // Для уникальности
    
    public Interaction() {}
    
    public Interaction(User fromUser, User toUser, InteractionType type) {
        this.fromUser = fromUser;
        this.toUser = toUser;
        this.type = type;
        this.weight = type.getBaseWeight();
        this.timestamp = LocalDateTime.now();
        this.contentHash = generateHash();
    }
    
    private String generateHash() {
        return fromUser.getId() + "_" + toUser.getId() + "_" + 
               type.name() + "_" + timestamp.toEpochSecond();
    }
    
    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public User getFromUser() { return fromUser; }
    public void setFromUser(User fromUser) { this.fromUser = fromUser; }
    
    public User getToUser() { return toUser; }
    public void setToUser(User toUser) { this.toUser = toUser; }
    
    public InteractionType getType() { return type; }
    public void setType(InteractionType type) { 
        this.type = type;
        this.weight = type.getBaseWeight();
    }
    
    public Double getWeight() { return weight; }
    public void setWeight(Double weight) { this.weight = weight; }
    
    public LocalDateTime getTimestamp() { return timestamp; }
    public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
    
    public String getContentHash() { return contentHash; }
}

InteractionType.java:

package com.socialdating.model;

public enum InteractionType {
    // Веса для разных типов взаимодействий
    MENTION(0.8, "Упоминание @username"),
    REPLY(0.7, "Ответ на сообщение"),
    FORWARD(0.9, "Пересылка сообщения"),
    REACTION(0.3, "Реакция (лайк, дизлайк)"),
    REPLY_TO_STORY(0.6, "Ответ на историю"),
    QUOTE(0.85, "Цитирование сообщения"),
    PIN(0.5, "Закрепление сообщения"),
    SAVE_MESSAGE(0.4, "Сохранение сообщения"),
    JOIN_VIA_LINK(0.2, "Вступление по ссылке пользователя"),
    DONATION(1.5, "Финансовый донат");
    
    private final double baseWeight;
    private final String description;
    
    InteractionType(double baseWeight, String description) {
        this.baseWeight = baseWeight;
        this.description = description;
    }
    
    public double getBaseWeight() {
        return baseWeight;
    }
    
    public String getDescription() {
        return description;
    }
    
    public static InteractionType fromString(String text) {
        for (InteractionType type : InteractionType.values()) {
            if (type.name().equalsIgnoreCase(text)) {
                return type;
            }
        }
        return REACTION; // По умолчанию
    }
}

4. Построение социального графа

SocialGraphBuilder.java:

package com.socialdating.analyzer;

import com.socialdating.model.User;
import com.socialdating.model.Interaction;
import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultDirectedWeightedGraph;
import org.jgrapht.graph.DefaultWeightedEdge;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;

public class SocialGraphBuilder {
    
    private final Map<Long, User> users = new HashMap<>();
    private final List<Interaction> interactions = new ArrayList<>();
    private final double timeDecayFactor; // Фактор временного затухания
    
    public SocialGraphBuilder(double timeDecayFactor) {
        this.timeDecayFactor = timeDecayFactor;
    }
    
    public void addUser(User user) {
        users.put(user.getId(), user);
    }
    
    public void addInteraction(Interaction interaction) {
        interactions.add(interaction);
        
        // Добавляем пользователей, если их еще нет
        if (!users.containsKey(interaction.getFromUser().getId())) {
            users.put(interaction.getFromUser().getId(), interaction.getFromUser());
        }
        if (!users.containsKey(interaction.getToUser().getId())) {
            users.put(interaction.getToUser().getId(), interaction.getToUser());
        }
    }
    
    public Graph<User, DefaultWeightedEdge> buildGraph(LocalDateTime analysisDate) {
        Graph<User, DefaultWeightedEdge> graph = 
            new DefaultDirectedWeightedGraph<>(DefaultWeightedEdge.class);
        
        // Добавляем вершины (пользователей)
        for (User user : users.values()) {
            graph.addVertex(user);
        }
        
        // Добавляем рёбра (взаимодействия) с учётом времени
        for (Interaction interaction : interactions) {
            User from = users.get(interaction.getFromUser().getId());
            User to = users.get(interaction.getToUser().getId());
            
            if (from != null && to != null && !from.equals(to)) {
                double timeWeight = calculateTimeDecay(interaction.getTimestamp(), analysisDate);
                double finalWeight = interaction.getWeight() * timeWeight;
                
                if (finalWeight > 0.01) { // Игнорируем слишком старые взаимодействия
                    DefaultWeightedEdge edge = graph.getEdge(from, to);
                    
                    if (edge == null) {
                        edge = graph.addEdge(from, to);
                        graph.setEdgeWeight(edge, finalWeight);
                    } else {
                        // Агрегируем веса для повторных взаимодействий
                        double currentWeight = graph.getEdgeWeight(edge);
                        graph.setEdgeWeight(edge, currentWeight + finalWeight);
                    }
                }
            }
        }
        
        return graph;
    }
    
    private double calculateTimeDecay(LocalDateTime eventTime, LocalDateTime currentTime) {
        long daysBetween = ChronoUnit.DAYS.between(eventTime, currentTime);
        
        // Экспоненциальное затухание: вес = e^(-λ * days)
        // где λ = timeDecayFactor (например, 0.01 = 1% затухания в день)
        return Math.exp(-timeDecayFactor * daysBetween);
    }
    
    public Map<Long, User> getUsers() {
        return Collections.unmodifiableMap(users);
    }
    
    public List<Interaction> getInteractions() {
        return Collections.unmodifiableList(interactions);
    }
}

5. Калькулятор Social Dating Rank

DatingRankCalculator.java:

package com.socialdating.analyzer;

import com.socialdating.model.User;
import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultWeightedEdge;

import java.util.HashMap;
import java.util.Map;

public class DatingRankCalculator {
    
    private static final double DAMPING_FACTOR = 0.85;
    private static final double CONVERGENCE_THRESHOLD = 0.0001;
    private static final int MAX_ITERATIONS = 100;
    
    public Map<User, Double> calculatePageRank(Graph<User, DefaultWeightedEdge> graph) {
        int vertexCount = graph.vertexSet().size();
        if (vertexCount == 0) {
            return new HashMap<>();
        }
        
        // Инициализация
        Map<User, Double> currentRank = new HashMap<>();
        double initialRank = 1.0 / vertexCount;
        
        for (User user : graph.vertexSet()) {
            currentRank.put(user, initialRank);
        }
        
        // Итеративный расчёт PageRank
        for (int iteration = 0; iteration < MAX_ITERATIONS; iteration++) {
            Map<User, Double> nextRank = new HashMap<>();
            
            // Базовая часть (телепортация)
            double teleportation = (1.0 - DAMPING_FACTOR) / vertexCount;
            
            for (User user : graph.vertexSet()) {
                nextRank.put(user, teleportation);
            }
            
            // Распространение ранга по рёбрам
            for (User source : graph.vertexSet()) {
                double sourceRank = currentRank.get(source);
                double totalOutgoingWeight = 0.0;
                
                // Считаем общий вес исходящих рёбер
                for (DefaultWeightedEdge edge : graph.outgoingEdgesOf(source)) {
                    totalOutgoingWeight += graph.getEdgeWeight(edge);
                }
                
                if (totalOutgoingWeight > 0) {
                    // Распределяем ранг источника по исходящим рёбрам
                    for (DefaultWeightedEdge edge : graph.outgoingEdgesOf(source)) {
                        User target = graph.getEdgeTarget(edge);
                        double edgeWeight = graph.getEdgeWeight(edge);
                        double contribution = DAMPING_FACTOR * sourceRank * 
                                            (edgeWeight / totalOutgoingWeight);
                        
                        nextRank.put(target, nextRank.get(target) + contribution);
                    }
                } else {
                    // Если нет исходящих рёбер - распределяем ранг равномерно
                    double distributedRank = DAMPING_FACTOR * sourceRank / vertexCount;
                    for (User target : graph.vertexSet()) {
                        nextRank.put(target, nextRank.get(target) + distributedRank);
                    }
                }
            }
            
            // Проверка сходимости
            if (hasConverged(currentRank, nextRank)) {
                currentRank = nextRank;
                break;
            }
            
            currentRank = nextRank;
            
            // Нормализация
            normalizeRanks(currentRank);
        }
        
        return currentRank;
    }
    
    private boolean hasConverged(Map<User, Double> oldRank, Map<User, Double> newRank) {
        double totalDiff = 0.0;
        
        for (User user : oldRank.keySet()) {
            double diff = Math.abs(oldRank.get(user) - newRank.get(user));
            totalDiff += diff;
        }
        
        return totalDiff < CONVERGENCE_THRESHOLD;
    }
    
    private void normalizeRanks(Map<User, Double> ranks) {
        double sum = ranks.values().stream().mapToDouble(Double::doubleValue).sum();
        
        if (sum > 0) {
            for (User user : ranks.keySet()) {
                ranks.put(user, ranks.get(user) / sum);
            }
        }
    }
    
    public Map<User, Double> calculateWithPersonalization(
            Graph<User, DefaultWeightedEdge> graph, 
            Map<User, Double> personalization) {
        
        Map<User, Double> baseRanks = calculatePageRank(graph);
        
        // Применяем персонализацию (например, учитываем количество подписчиков)
        for (User user : baseRanks.keySet()) {
            double personalizationFactor = 1.0;
            
            if (personalization.containsKey(user)) {
                personalizationFactor = personalization.get(user);
            } else if (user.getFollowerCount() != null) {
                // Логарифмическая шкала для количества подписчиков
                personalizationFactor = Math.log1p(user.getFollowerCount()) / 10.0 + 1.0;
            }
            
            baseRanks.put(user, baseRanks.get(user) * personalizationFactor);
        }
        
        normalizeRanks(baseRanks);
        return baseRanks;
    }
}

6. Telegram бот для управления

TelegramBot.java:

package com.socialdating.bot;

import com.socialdating.analyzer.DatingRankCalculator;
import com.socialdating.analyzer.SocialGraphBuilder;
import com.socialdating.model.User;
import com.socialdating.model.Interaction;
import com.socialdating.model.InteractionType;
import com.socialdating.storage.UserRepository;
import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultWeightedEdge;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.User as TelegramUser;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class TelegramBot extends TelegramLongPollingBot {
    
    private final String botToken;
    private final String botUsername;
    private final UserRepository userRepository;
    private final SocialGraphBuilder graphBuilder;
    private final DatingRankCalculator rankCalculator;
    
    private final Map<Long, LocalDateTime> lastUserActivity = new ConcurrentHashMap<>();
    private final Map<String, List<Interaction>> pendingInteractions = new ConcurrentHashMap<>();
    
    public TelegramBot(String botToken, String botUsername, UserRepository userRepository) {
        this.botToken = botToken;
        this.botUsername = botUsername;
        this.userRepository = userRepository;
        this.graphBuilder = new SocialGraphBuilder(0.01); // 1% затухание в день
        this.rankCalculator = new DatingRankCalculator();
    }
    
    @Override
    public void onUpdateReceived(Update update) {
        if (update.hasMessage()) {
            handleMessage(update.getMessage());
        } else if (update.hasCallbackQuery()) {
            handleCallbackQuery(update.getCallbackQuery());
        }
    }
    
    private void handleMessage(Message message) {
        TelegramUser tgUser = message.getFrom();
        Long chatId = message.getChatId();
        
        // Сохраняем/обновляем пользователя
        User user = userRepository.findOrCreate(
            tgUser.getId(),
            tgUser.getUserName(),
            tgUser.getFirstName(),
            tgUser.getLastName()
        );
        
        lastUserActivity.put(user.getId(), LocalDateTime.now());
        
        // Анализируем сообщение на взаимодействия
        analyzeMessageForInteractions(message, user);
        
        // Обработка команд
        if (message.hasText()) {
            String text = message.getText().toLowerCase();
            
            if (text.startsWith("/start")) {
                sendWelcomeMessage(chatId, user);
            } else if (text.startsWith("/myrank")) {
                sendUserRank(chatId, user);
            } else if (text.startsWith("/top")) {
                sendTopUsers(chatId, extractTopCount(text));
            } else if (text.startsWith("/analyze")) {
                analyzeAndSendResults(chatId);
            } else if (text.startsWith("/help")) {
                sendHelpMessage(chatId);
            }
        }
    }
    
    private void analyzeMessageForInteractions(Message message, User fromUser) {
        String text = message.getText();
        if (text == null) return;
        
        // Поиск упоминаний (@username)
        String[] words = text.split("\\s+");
        for (String word : words) {
            if (word.startsWith("@") && word.length() > 1) {
                String username = word.substring(1);
                User mentionedUser = userRepository.findByUsername(username);
                
                if (mentionedUser != null && !mentionedUser.equals(fromUser)) {
                    Interaction interaction = new Interaction(
                        fromUser, 
                        mentionedUser, 
                        InteractionType.MENTION
                    );
                    graphBuilder.addInteraction(interaction);
                }
            }
        }
        
        // Если это ответ на сообщение
        if (message.getReplyToMessage() != null) {
            Message repliedTo = message.getReplyToMessage();
            if (repliedTo.getFrom() != null) {
                TelegramUser repliedUser = repliedTo.getFrom();
                User toUser = userRepository.findOrCreate(
                    repliedUser.getId(),
                    repliedUser.getUserName(),
                    repliedUser.getFirstName(),
                    repliedUser.getLastName()
                );
                
                if (!toUser.equals(fromUser)) {
                    Interaction interaction = new Interaction(
                        fromUser, 
                        toUser, 
                        InteractionType.REPLY
                    );
                    graphBuilder.addInteraction(interaction);
                }
            }
        }
    }
    
    private void analyzeAndSendResults(Long chatId) {
        try {
            // Строим граф
            Graph<User, DefaultWeightedEdge> graph = 
                graphBuilder.buildGraph(LocalDateTime.now());
            
            // Вычисляем ранги
            Map<User, Double> ranks = rankCalculator.calculatePageRank(graph);
            
            // Обновляем ранги пользователей
            for (Map.Entry<User, Double> entry : ranks.entrySet()) {
                User user = entry.getKey();
                user.setDatingRank(entry.getValue());
                userRepository.save(user);
            }
            
            // Формируем результат
            StringBuilder response = new StringBuilder();
            response.append("📊 *Результаты анализа Social Dating Rank*\n\n");
            response.append("Проанализировано пользователей: ").append(ranks.size()).append("\n");
            response.append("Взаимодействий: ").append(graphBuilder.getInteractions().size()).append("\n\n");
            response.append("*Топ-5 пользователей:*\n");
            
            ranks.entrySet().stream()
                .sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
                .limit(5)
                .forEach(entry -> {
                    User user = entry.getKey();
                    response.append("👤 ")
                           .append(user.getUsername() != null ? "@" + user.getUsername() : user.getFirstName())
                           .append(": ")
                           .append(String.format("%.6f", entry.getValue()))
                           .append("\n");
                });
            
            sendMessage(chatId, response.toString(), true);
            
        } catch (Exception e) {
            sendMessage(chatId, "❌ Ошибка при анализе: " + e.getMessage(), false);
        }
    }
    
    private void sendUserRank(Long chatId, User user) {
        double rank = user.getDatingRank() != null ? user.getDatingRank() : 1.0;
        
        String response = String.format(
            "👤 *Ваш Social Dating Rank*\n\n" +
            "Имя: %s\n" +
            "Username: %s\n" +
            "Ранг: %.6f\n" +
            "Место в рейтинге: #%d\n\n" +
            "Ранг показывает ваше влияние в сети на основе взаимодействий с другими пользователями.",
            user.getFirstName(),
            user.getUsername() != null ? "@" + user.getUsername() : "не указан",
            rank,
            calculateUserPosition(user.getId())
        );
        
        sendMessage(chatId, response, true);
    }
    
    private int calculateUserPosition(Long userId) {
        // Здесь должна быть логика получения позиции из БД
        return 1; // Заглушка
    }
    
    private void sendTopUsers(Long chatId, int count) {
        List<User> topUsers = userRepository.findTopUsers(count);
        
        if (topUsers.isEmpty()) {
            sendMessage(chatId, "📊 Рейтинг пока пуст. Используйте /analyze для анализа.", false);
            return;
        }
        
        StringBuilder response = new StringBuilder();
        response.append("🏆 *Топ-").append(count).append(" пользователей*\n\n");
        
        for (int i = 0; i < topUsers.size(); i++) {
            User user = topUsers.get(i);
            response.append(i + 1).append(". ")
                   .append(user.getUsername() != null ? "@" + user.getUsername() : user.getFirstName())
                   .append(" - ")
                   .append(String.format("%.6f", user.getDatingRank()))
                   .append("\n");
        }
        
        sendMessage(chatId, response.toString(), true);
    }
    
    private int extractTopCount(String text) {
        try {
            String[] parts = text.split(" ");
            if (parts.length > 1) {
                return Integer.parseInt(parts[1]);
            }
        } catch (NumberFormatException e) {
            // Игнорируем
        }
        return 10; // По умолчанию
    }
    
    private void sendWelcomeMessage(Long chatId, User user) {
        String welcome = String.format(
            "👋 Привет, %s!\n\n" +
            "Я бот для анализа *Social Dating Rank* в Telegram.\n\n" +
            "📊 *Социальный рейтинг* показывает ваше влияние в сети на основе:\n" +
            "• Упоминаний (@username)\n" +
            "• Ответов на сообщения\n" +
            "• Репостов и реакций\n" +
            "• Других взаимодействий\n\n" +
            "📋 *Доступные команды:*\n" +
            "/myrank - ваш текущий рейтинг\n" +
            "/top [N] - топ-N пользователей\n" +
            "/analyze - запустить анализ\n" +
            "/help - помощь\n\n" +
            "Бот автоматически анализирует ваши взаимодействия в чатах!",
            user.getFirstName()
        );
        
        sendMessage(chatId, welcome, true);
    }
    
    private void sendHelpMessage(Long chatId) {
        String help = """
            📚 *Помощь по боту Social Dating Rank*
            
            *Как работает рейтинг:*
            Алгоритм анализирует ваши взаимодействия с другими пользователями:
            • Упоминания (@username) = 0.8
            • Ответы на сообщения = 0.7
            • Репосты = 0.9
            • Реакции = 0.3
            • и т.д.
            
            Ранг рассчитывается по модифицированному алгоритму PageRank, где:
            • Каждое взаимодействие имеет вес
            • Более свежие взаимодействия значимее
            • Взаимодействия с влиятельными пользователями ценятся выше
            
            *Команды:*
            /start - начать работу
            /myrank - ваш рейтинг
            /top 10 - топ-10 пользователей
            /analyze - запустить анализ (только для админов)
            /help - эта справка
            
            Бот работает в фоновом режиме и собирает данные из чатов, где он добавлен.
            """;
        
        sendMessage(chatId, help, true);
    }
    
    private void sendMessage(Long chatId, String text, boolean markdown) {
        SendMessage message = new SendMessage();
        message.setChatId(chatId.toString());
        message.setText(text);
        message.setParseMode(markdown ? "Markdown" : null);
        
        try {
            execute(message);
        } catch (TelegramApiException e) {
            System.err.println("Ошибка отправки сообщения: " + e.getMessage());
        }
    }
    
    @Override
    public String getBotUsername() {
        return botUsername;
    }
    
    @Override
    public String getBotToken() {
        return botToken;
    }
}

7. Хранилище данных (SQLite)

UserRepository.java:

package com.socialdating.storage;

import com.socialdating.model.User;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class UserRepository {
    private final Connection connection;
    
    public UserRepository(String dbPath) throws SQLException {
        String url = "jdbc:sqlite:" + dbPath;
        connection = DriverManager.getConnection(url);
        createTables();
    }
    
    private void createTables() throws SQLException {
        String createUsersTable = """
            CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY,
                username TEXT,
                first_name TEXT NOT NULL,
                last_name TEXT,
                follower_count INTEGER DEFAULT 0,
                joined_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                dating_rank REAL DEFAULT 1.0,
                last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
            """;
        
        String createInteractionsTable = """
            CREATE TABLE IF NOT EXISTS interactions (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                from_user_id INTEGER NOT NULL,
                to_user_id INTEGER NOT NULL,
                interaction_type TEXT NOT NULL,
                weight REAL NOT NULL,
                timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                content_hash TEXT UNIQUE,
                FOREIGN KEY (from_user_id) REFERENCES users(id),
                FOREIGN KEY (to_user_id) REFERENCES users(id)
            )
            """;
        
        String createIndex = """
            CREATE INDEX IF NOT EXISTS idx_interactions_users 
            ON interactions(from_user_id, to_user_id, timestamp)
            """;
        
        try (Statement stmt = connection.createStatement()) {
            stmt.execute(createUsersTable);
            stmt.execute(createInteractionsTable);
            stmt.execute(createIndex);
        }
    }
    
    public User findOrCreate(Long id, String username, String firstName, String lastName) {
        String selectSql = "SELECT * FROM users WHERE id = ?";
        String insertSql = """
            INSERT OR IGNORE INTO users 
            (id, username, first_name, last_name) 
            VALUES (?, ?, ?, ?)
            """;
        
        try (PreparedStatement selectStmt = connection.prepareStatement(selectSql)) {
            selectStmt.setLong(1, id);
            ResultSet rs = selectStmt.executeQuery();
            
            if (rs.next()) {
                return mapUserFromResultSet(rs);
            } else {
                try (PreparedStatement insertStmt = connection.prepareStatement(insertSql)) {
                    insertStmt.setLong(1, id);
                    insertStmt.setString(2, username);
                    insertStmt.setString(3, firstName);
                    insertStmt.setString(4, lastName);
                    insertStmt.executeUpdate();
                    
                    return new User(id, username, firstName);
                }
            }
        } catch (SQLException e) {
            System.err.println("Ошибка поиска/создания пользователя: " + e.getMessage());
            return new User(id, username, firstName);
        }
    }
    
    public void save(User user) {
        String sql = """
            UPDATE users SET 
                username = ?, 
                first_name = ?, 
                last_name = ?,
                follower_count = ?,
                dating_rank = ?,
                last_updated = CURRENT_TIMESTAMP
            WHERE id = ?
            """;
        
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setString(1, user.getUsername());
            stmt.setString(2, user.getFirstName());
            stmt.setString(3, user.getLastName());
            stmt.setInt(4, user.getFollowerCount() != null ? user.getFollowerCount() : 0);
            stmt.setDouble(5, user.getDatingRank() != null ? user.getDatingRank() : 1.0);
            stmt.setLong(6, user.getId());
            stmt.executeUpdate();
        } catch (SQLException e) {
            System.err.println("Ошибка сохранения пользователя: " + e.getMessage());
        }
    }
    
    public List<User> findTopUsers(int limit) {
        List<User> users = new ArrayList<>();
        String sql = "SELECT * FROM users WHERE dating_rank > 0 ORDER BY dating_rank DESC LIMIT ?";
        
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setInt(1, limit);
            ResultSet rs = stmt.executeQuery();
            
            while (rs.next()) {
                users.add(mapUserFromResultSet(rs));
            }
        } catch (SQLException e) {
            System.err.println("Ошибка получения топ-пользователей: " + e.getMessage());
        }
        
        return users;
    }
    
    public User findByUsername(String username) {
        String sql = "SELECT * FROM users WHERE username = ?";
        
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setString(1, username);
            ResultSet rs = stmt.executeQuery();
            
            if (rs.next()) {
                return mapUserFromResultSet(rs);
            }
        } catch (SQLException e) {
            System.err.println("Ошибка поиска по username: " + e.getMessage());
        }
        
        return null;
    }
    
    private User mapUserFromResultSet(ResultSet rs) throws SQLException {
        User user = new User();
        user.setId(rs.getLong("id"));
        user.setUsername(rs.getString("username"));
        user.setFirstName(rs.getString("first_name"));
        user.setLastName(rs.getString("last_name"));
        user.setFollowerCount(rs.getInt("follower_count"));
        user.setJoinedDate(rs.getTimestamp("joined_date").toLocalDateTime());
        user.setDatingRank(rs.getDouble("dating_rank"));
        return user;
    }
    
    public void close() {
        try {
            if (connection != null && !connection.isClosed()) {
                connection.close();
            }
        } catch (SQLException e) {
            System.err.println("Ошибка закрытия соединения: " + e.getMessage());
        }
    }
}

8. Главный класс приложения

Main.java:

package com.socialdating;

import com.socialdating.bot.TelegramBot;
import com.socialdating.storage.UserRepository;
import org.telegram.telegrambots.meta.TelegramBotsApi;
import org.telegram.telegrambots.updatesreceivers.DefaultBotSession;

import java.util.Scanner;

public class Main {
    
    private static final String CONFIG_FILE = "config.properties";
    
    public static void main(String[] args) {
        try {
            System.out.println("🚀 Запуск Social Dating Rank Analyzer для Telegram");
            System.out.println("===================================================\n");
            
            // Загрузка конфигурации
            Config config = loadConfig();
            
            // Инициализация хранилища
            UserRepository userRepository = new UserRepository(config.getDbPath());
            System.out.println("✅ База данных инициали

Comments

Sign in to leave a comment
Loading files...
Loading attachments...