This commit is contained in:
Wain 2024-02-11 15:14:28 +03:00 committed by GitHub
parent 2c73c4baec
commit 400eb896b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
98 changed files with 5032 additions and 0 deletions

7
README.md Normal file
View File

@ -0,0 +1,7 @@
# Bunker
Bunker - telegram bot, that provides ability to play a post-apocalypse survival game.
## Description
This bot acts as a "game master", providing unique game experience for you and your friends.

113
pom.xml Normal file
View File

@ -0,0 +1,113 @@
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version>
</parent>
<groupId>com.alterdekim.javabot</groupId>
<artifactId>bunker</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
<!-- Temporary explicit version to fix Thymeleaf bug -->
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
<version>1.18.28</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots-spring-boot-starter</artifactId>
<version>6.7.0</version>
</dependency>
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots-abilities</artifactId>
<version>6.7.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,25 @@
package com.alterdekim.javabot;
import com.alterdekim.javabot.storage.StorageService;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
CommandLineRunner init(StorageService storageService) {
return (args) -> {
storageService.init();
};
}
}

View File

@ -0,0 +1,10 @@
package com.alterdekim.javabot;
public interface Commands {
String SET_GROUP = "/setgroup";
String GET_INFO = "/info";
String STOP_GAME = "/stopgame";
String START_GAME = "/startgame";
String JOIN_GAME = "/join";
String HELP = "/help";
}

View File

@ -0,0 +1,54 @@
package com.alterdekim.javabot;
public interface Constants {
String REMOVE_PLAYER = "Игрок %s покидает бункер.";
String ENDVOTE = "Голосование окончено.";
String DRAW = "Ничья. Никто не уходит из игры.";
String GROUP_SET = "Чат выбран.";
String INTERRUPT_GAME = "Игра остановлена.";
String USER_VOTED = "%s проголосовал.";
String JOIN_GAME_BTN = "Присоединиться";
String START_GAME_MSG = "Набор игроков начат. Присоединяйтесь.";
String JOINED_THE_GAME = "%s добавлен(а) в игру. Всего игроков: %d";
String THANK_YOU = "Спасибо.";
String ALREADY_IN_GAME = "Ты уже в игре.";
String CANT_JOIN_NOT_STARTED = "Не могу добавить вас в игру, тк вы не разрешили вам писать сообщения.";
String HELP = "Help";
String START_GAME_BTN = "Начать игру";
String NOT_ADMIN_EXCEPTION = "Вы не администратор";
String ACCOUNT = "Ваша анкета:\ол: %s (смертный: %s, мужчина: %s, женщина: %s)\nВозраст: %d\рофессия: %s (%s)\агаж: %s (%s)\nХобби: %s\nЗдоровье: %s (%s) %d%%, бесплоден: %s";
String TRUE = "да";
String FALSE = "нет";
String PLAYERS_LESS_THAN_ZERO = "Игроков должно быть больше, чем 1.";
String HOBBY_BTN = "Хобби";
String WORK_BTN = "Работа";
String HEALTH_BTN = "Здоровье";
String AGE_BTN = "Возраст";
String GENDER_BTN = "Гендер";
String LUGG_BTN = "Багаж";
String GENDER_MESAGE = "%s - пол: %s (смертный: %s, мужчина: %s, женщина: %s)";
String HEALTH_MESSAGE = "%s - здоровье: %s (%s) %d%%, бесплоден(а): %s";
String SHOW_TIME = "Время выбрать, какую часть анкеты показать в этом раунде?";
String AGE_MESSAGE = "%s - возраст: %d";
String HOBBY_MESSAGE = "%s - хобби: %s";
String WORK_MESSAGE = "%s - профессия: %s (%s)";
String LUGG_MESSAGE = "%s - багаж: %s (%s)";
String DAY_MESSAGE_UPPER = "Следующий раунд начался!\nВероятность выжить \uD83D\uDCC8: %d%% (увеличилась на %f%%)";
String PRESSED_NIGHT = "%s нажал(а)";
String DAY_MESSAGE_DOWN = "Следующий раунд начался!\nВероятность выжить \uD83D\uDCC9: %d%% (уменьшилась на %f%%)";
String DAY_MESSAGE = "Следующий раунд начался!\nВероятность выжить: %f%%";
String END_GAME = "Конец игры.\nВероятность выжить: %f%%";
String POLL_QUESTION = "Кто в бункер не идёт?";
String DEATHMATCH = "Deathmatch";
String PROBABILITY = "Обычная";
String WIN_MESSAGE = "Поздравляю! Победа за вами!\n%s";
String LOSE_MESSAGE = "Поздравляю! Вы проиграли!";
String INFO_MESSAGE = "Вот открытая информация о живых игроках\n";
String CANT_SEND_NOT_DAY = "Нельзя использовать эту команду во время перерыва.";
}

View File

@ -0,0 +1,41 @@
package com.alterdekim.javabot;
import com.alterdekim.javabot.storage.StorageFileNotFoundException;
import com.alterdekim.javabot.storage.StorageProperties;
import com.alterdekim.javabot.storage.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@EnableConfigurationProperties(StorageProperties.class)
public class FileServerController {
private final StorageService storageService;
@Autowired
public FileServerController(StorageService storageService) {
this.storageService = storageService;
}
@GetMapping("/static/{assetspath}/{filename:.+}")
@ResponseBody
public ResponseEntity<Resource> serveFile(@PathVariable String filename, @PathVariable String assetspath) {
Resource file = storageService.loadAsResource("static/" + assetspath + "/" + filename);
return ResponseEntity.ok().contentType(new MediaType("text", assetspath)).header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + file.getFilename() + "\"").body(file);
}
@ExceptionHandler(StorageFileNotFoundException.class)
public ResponseEntity<?> handleStorageFileNotFound(StorageFileNotFoundException exc) {
return ResponseEntity.notFound().build();
}
}

View File

@ -0,0 +1,28 @@
package com.alterdekim.javabot;
import com.alterdekim.javabot.components.BunkerBot;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.meta.TelegramBotsApi;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
import org.telegram.telegrambots.updatesreceivers.DefaultBotSession;
@Component
@Slf4j
public class Initializer {
@Autowired
BunkerBot bot;
@EventListener({ContextRefreshedEvent.class})
public void init() {
try {
TelegramBotsApi telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class);
telegramBotsApi.registerBot(bot);
} catch (TelegramApiException e) {
log.error(e.getMessage());
}
}
}

View File

@ -0,0 +1,41 @@
package com.alterdekim.javabot;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import java.util.Locale;
@Configuration
public class MvcConfig implements WebMvcConfigurer {
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/signup").setViewName("signup");
registry.addViewController("/login").setViewName("login");
registry.addViewController("/panel").setViewName("panel");
registry.addRedirectViewController("/", "/panel");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(Locale.US);
return slr;
}
}

View File

@ -0,0 +1,16 @@
package com.alterdekim.javabot;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Data
@Configuration
@PropertySource("file:./telegram.properties") // classpath:/telegram.properties
public class TelegramConfig {
@Value("${bot.botToken}") private String botToken;
@Value("${bot.botLogin}") private String botLogin;
@Value("${bot.master}") private String masterId;
}

View File

@ -0,0 +1,30 @@
package com.alterdekim.javabot.bot;
import com.alterdekim.javabot.Constants;
import com.alterdekim.javabot.service.TextDataValService;
public class BotAccountProfileGenerator {
public static String build(TextDataValService textDataValService, Player p) {
return String.format(Constants.ACCOUNT,
getStringById(textDataValService, p.getGender().getGenderTextId()),
p.getGender().getCanDie() ? Constants.TRUE : Constants.FALSE,
p.getGender().getIsMale() ? Constants.TRUE : Constants.FALSE,
p.getGender().getIsFemale() ? Constants.TRUE : Constants.FALSE,
p.getAge(),
getStringById(textDataValService, p.getWork().getTextNameId()),
getStringById(textDataValService, p.getWork().getTextDescId()),
getStringById(textDataValService, p.getLuggage().getTextNameId()),
getStringById(textDataValService, p.getLuggage().getTextDescId()),
getStringById(textDataValService, p.getHobby().getTextDescId()),
getStringById(textDataValService, p.getHealth().getTextNameId()),
getStringById(textDataValService, p.getHealth().getTextDescId()),
(int) (p.getHealth().getHealth_index()*100f),
p.getHealth().getIsChildfree() ? Constants.TRUE : Constants.FALSE
);
}
private static String getStringById(TextDataValService textDataValService, Long id) {
return textDataValService.getTextDataValById(id).getText();
}
}

View File

@ -0,0 +1,31 @@
package com.alterdekim.javabot.bot;
import lombok.Getter;
import lombok.Setter;
import java.util.HashMap;
import java.util.Map;
@Getter
@Setter
public class DayNightFields {
private Boolean isNight;
private Integer nightToken;
private String lastPollId;
private Map<Integer, Integer> poll;
private String dayMessage;
private Integer turnCount;
public DayNightFields() {
this.isNight = false;
this.nightToken = 0;
this.lastPollId = "";
this.poll = new HashMap<>();
this.dayMessage = "";
this.turnCount = 0;
}
public void appendMessage(String s) {
this.dayMessage += s;
}
}

View File

@ -0,0 +1,24 @@
package com.alterdekim.javabot.bot;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class InfoSections {
private Boolean isGenderShowed;
private Boolean isAgeShowed;
private Boolean isWorkShowed;
private Boolean isLuggageShowed;
private Boolean isHobbyShowed;
private Boolean isHealthShowed;
public InfoSections() {
this.isAgeShowed = false;
this.isLuggageShowed = false;
this.isGenderShowed = false;
this.isWorkShowed = false;
this.isHealthShowed = false;
this.isHobbyShowed = false;
}
}

View File

@ -0,0 +1,81 @@
package com.alterdekim.javabot.bot;
import com.alterdekim.javabot.entities.ColumnType;
import com.alterdekim.javabot.entities.Synergy;
import com.alterdekim.javabot.util.Clamp;
import java.util.List;
public class LiveFormula {
public static double calc(List<Player> playerList, List<Synergy> synergies) {
double i = 0;
for( Player p : playerList ) {
double age = 1.0D - (((double) p.getAge()) / 75.0D);
age = p.getGender().getCanDie() ? age : 1.0D;
double gender = p.getGender().getIsMale() ? 1 : 0;
gender += p.getGender().getIsFemale() ? 1 : 0;
gender = p.getHealth().getIsChildfree() ? 0 : gender;
gender *= 0.5D;
double work = p.getWork().getValue();
double luggage = p.getLuggage().getValue();
luggage = p.getLuggage().getGarbage() ? 0 : luggage;
double hobby = p.getHobby().getValue();
double health = p.getHealth().getHealth_index().doubleValue();
i += ((age + gender + work + luggage + hobby + health) / 6.0d);
}
i = i / ((double) playerList.size());
double _i = i;
if( playerList.stream().anyMatch(t -> (t.getGender().getIsMale() && !t.getHealth().getIsChildfree())) &&
playerList.stream().anyMatch(t -> (t.getGender().getIsFemale() && !t.getHealth().getIsChildfree())) ) {
i += 0.3 * _i;
}
for( Synergy s : synergies ) {
Boolean fb = LiveFormula.entity(playerList, s.getFirstType(), s.getFirstEntityId());
Boolean eb = LiveFormula.entity(playerList, s.getSecondType(), s.getSecondEntityId());
if( fb && eb ) i += s.getProbabilityValue().doubleValue() * _i;
}
return Clamp.clamp(i * 1.2d, 0, 1);
}
private static Boolean entity(List<Player> players, ColumnType ct, Long eid) {
Boolean fb = false;
switch (ct) {
case Bio:
fb = LiveFormula.searchForBio(players, eid);
break;
case Work:
fb = LiveFormula.searchForWork(players, eid);
break;
case Hobby:
fb = LiveFormula.searchForHobby(players, eid);
break;
case Health:
fb = LiveFormula.searchForHealth(players, eid);
break;
case Luggage:
fb = LiveFormula.searchForLuggage(players, eid);
break;
}
return fb;
}
private static Boolean searchForBio(List<Player> players, Long id) {
return players.stream().anyMatch(p -> p.getGender().getId().equals(id));
}
private static Boolean searchForWork(List<Player> players, Long id) {
return players.stream().anyMatch(p -> p.getWork().getId().equals(id));
}
private static Boolean searchForHobby(List<Player> players, Long id) {
return players.stream().anyMatch(p -> p.getHobby().getId().equals(id));
}
private static Boolean searchForHealth(List<Player> players, Long id) {
return players.stream().anyMatch(p -> p.getHealth().getId().equals(id));
}
private static Boolean searchForLuggage(List<Player> players, Long id) {
return players.stream().anyMatch(p -> p.getLuggage().getId().equals(id));
}
}

View File

@ -0,0 +1,29 @@
package com.alterdekim.javabot.bot;
import com.alterdekim.javabot.entities.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
public class Player {
private Long telegramId;
private int age;
private Bio gender;
private Health health;
private Hobby hobby;
private Work work;
private Luggage luggage;
private Boolean isAnswered = false;
private String firstName;
private InfoSections infoSections;
private Boolean isVoted = false;
public Player(Long telegramId, String name) {
this.telegramId = telegramId;
this.firstName = name;
this.infoSections = new InfoSections();
}
}

View File

@ -0,0 +1,513 @@
package com.alterdekim.javabot.components;
import com.alterdekim.javabot.bot.*;
import com.alterdekim.javabot.Commands;
import com.alterdekim.javabot.Constants;
import com.alterdekim.javabot.TelegramConfig;
import com.alterdekim.javabot.entities.*;
import com.alterdekim.javabot.service.*;
import com.alterdekim.javabot.util.BotUtils;
import com.alterdekim.javabot.util.GameState;
import com.alterdekim.javabot.util.HashUtils;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.api.methods.AnswerCallbackQuery;
import org.telegram.telegrambots.meta.api.methods.BotApiMethod;
import org.telegram.telegrambots.meta.api.methods.groupadministration.GetChatMember;
import org.telegram.telegrambots.meta.api.methods.polls.SendPoll;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.methods.updatingmessages.DeleteMessage;
import org.telegram.telegrambots.meta.api.objects.CallbackQuery;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.api.objects.chatmember.ChatMember;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
@Component
@Slf4j
public class BunkerBot extends TelegramLongPollingBot {
private final TelegramConfig telegramConfig;
private String groupId = "";
private GameState gameState;
private double last_p = -1;
private List<Player> players;
private final BioService bioService;
private final HealthService healthService;
private final HobbyService hobbyService;
private final LuggageService luggageService;
private final WorkService workService;
private final TextDataValService textDataValService;
private final DisasterService disasterService;
private final SynergyService synergyService;
private final Random random;
private DayNightFields dayNightFields;
private ConcurrentLinkedQueue<BotApiMethod<? extends Serializable>> linkedQueue;
@SuppressWarnings("deprecation")
public BunkerBot(TelegramConfig telegramConfig,
BioService bioService,
HealthService healthService,
HobbyService hobbyService,
LuggageService luggageService,
WorkService workService,
TextDataValService textDataValService,
DisasterService disasterService,
SynergyService synergyService) {
this.telegramConfig = telegramConfig;
this.players = new ArrayList<>();
this.gameState = GameState.NONE;
this.bioService = bioService;
this.healthService = healthService;
this.hobbyService = hobbyService;
this.luggageService = luggageService;
this.workService = workService;
this.textDataValService = textDataValService;
this.disasterService = disasterService;
this.synergyService = synergyService;
this.random = new Random();
this.dayNightFields = new DayNightFields();
this.linkedQueue = new ConcurrentLinkedQueue<>();
}
@Scheduled(fixedRate = 100)
private void executeApi() {
if( !this.linkedQueue.isEmpty() )
sendApiMethodAsync(this.linkedQueue.poll());
}
@Override
public String getBotUsername() {
return telegramConfig.getBotLogin();
}
@Override
@SuppressWarnings("deprecation")
public String getBotToken() {
return telegramConfig.getBotToken();
}
private Long getMasterId() {
return Long.parseLong(telegramConfig.getMasterId());
}
private void joinGame(User user, Integer msgid) {
if( gameState != GameState.JOINING )
return;
if( !hasPlayerWithId(user.getId()) ) {
if( canWrite(user) ) {
this.players.add(new Player(user.getId(), user.getFirstName()));
sendApi(
new SendMessage(
groupId,
String.format(
Constants.JOINED_THE_GAME,
user.getFirstName(),
players.size()
)
)
);
return;
}
if( msgid != 0 ) {
SendMessage sm = new SendMessage(groupId, Constants.CANT_JOIN_NOT_STARTED);
sm.setReplyToMessageId(msgid);
sendApi(sm);
return;
}
SendMessage sm = new SendMessage(groupId, BotUtils.mentionUser(user.getUserName()) + Constants.CANT_JOIN_NOT_STARTED);
sendApi(sm);
return;
}
sendApi(
new SendMessage(
groupId,
Constants.ALREADY_IN_GAME
)
);
}
private Boolean canWrite(User u) {
SendMessage message = new SendMessage(u.getId()+"", ".");
message.disableNotification();
Message mr;
try {
mr = sendApiMethod(message);
} catch ( Exception e ) {
return false;
}
sendApi(new DeleteMessage(u.getId()+"", mr.getMessageId()));
return true;
}
private Boolean isAdmin(User u) {
GetChatMember chatMember = new GetChatMember(groupId, u.getId());
try {
ChatMember cm = sendApiMethod(chatMember);
return cm.getStatus().equals("creator") || cm.getStatus().startsWith("admin");
} catch (Exception e) {
return false;
}
}
private void startGame() {
if( gameState != GameState.JOINING )
return;
if(players.size() < 2) {
sendApi(new SendMessage(groupId, Constants.PLAYERS_LESS_THAN_ZERO));
return;
}
this.gameState = GameState.STARTED;
Disaster d = (Disaster) BotUtils.getRandomFromList(disasterService.getAllDisasters(), random);
sendApi(new SendMessage(groupId, getStringById(d.getDescTextId())));
List<Bio> bios = bioService.getAllBios();
List<Work> works = workService.getAllWorks();
List<Luggage> luggs = luggageService.getAllLuggages();
List<Hobby> hobbies = hobbyService.getAllHobbies();
List<Health> healths = healthService.getAllHealth();
for( Player p : players ) {
p.setAge(random.nextInt(57)+18);
p.setGender((Bio) BotUtils.getRandomFromList(bios, random));
p.setWork((Work) BotUtils.getRandomFromList(works, random));
p.setLuggage((Luggage) BotUtils.getRandomFromList(luggs, random));
p.setHobby((Hobby) BotUtils.getRandomFromList(hobbies, random));
p.setHealth((Health) BotUtils.getRandomFromList(healths, random));
SendMessage sendMessage = new SendMessage(p.getTelegramId()+"", BotAccountProfileGenerator.build(textDataValService, p));
sendApi(sendMessage);
}
doNight();
}
private void doNight() {
this.dayNightFields.setIsNight(true);
this.dayNightFields.setNightToken(random.nextInt(1000)+10);
this.dayNightFields.setPoll(new HashMap<>());
this.dayNightFields.setTurnCount(this.dayNightFields.getTurnCount()+1);
for( Player p : players ) {
SendMessage sendMessage = new SendMessage(p.getTelegramId()+"", Constants.SHOW_TIME);
sendMessage.setReplyMarkup(BotUtils.getShowKeyboard(p.getInfoSections()));
sendApi(sendMessage);
}
}
private String getStringById(Long id) {
return textDataValService.getTextDataValById(id).getText();
}
private void processNightButton(CallbackQuery callbackQuery) {
if( this.hasPlayerWithId(callbackQuery.getFrom().getId()) &&
!getPlayerById(callbackQuery.getFrom().getId()).getIsAnswered() ) {
Player p = getPlayerById(callbackQuery.getFrom().getId());
InfoSections ins = p.getInfoSections();
switch (new String(HashUtils.decodeHexString(callbackQuery.getData()))) {
case Constants.GENDER_BTN:
dayNightFields.appendMessage(String.format(Constants.GENDER_MESAGE, callbackQuery.getFrom().getFirstName(), getStringById(p.getGender().getGenderTextId()),
p.getGender().getCanDie() ? Constants.TRUE : Constants.FALSE,
p.getGender().getIsMale() ? Constants.TRUE : Constants.FALSE,
p.getGender().getIsFemale() ? Constants.TRUE : Constants.FALSE) + "\n");
ins.setIsGenderShowed(true);
break;
case Constants.HEALTH_BTN:
dayNightFields.appendMessage(String.format(Constants.HEALTH_MESSAGE, callbackQuery.getFrom().getFirstName(), getStringById(p.getHealth().getTextNameId()),
getStringById(p.getHealth().getTextDescId()),
(int) (p.getHealth().getHealth_index()*100f),
p.getHealth().getIsChildfree() ? Constants.TRUE : Constants.FALSE) + "\n");
ins.setIsHealthShowed(true);
break;
case Constants.AGE_BTN:
dayNightFields.appendMessage(String.format(Constants.AGE_MESSAGE, callbackQuery.getFrom().getFirstName(), p.getAge()) + "\n");
ins.setIsAgeShowed(true);
break;
case Constants.HOBBY_BTN:
dayNightFields.appendMessage(String.format(Constants.HOBBY_MESSAGE, callbackQuery.getFrom().getFirstName(),
getStringById(p.getHobby().getTextDescId())) + "\n");
ins.setIsHobbyShowed(true);
break;
case Constants.LUGG_BTN:
dayNightFields.appendMessage(String.format(Constants.LUGG_MESSAGE, callbackQuery.getFrom().getFirstName(),
getStringById(p.getLuggage().getTextNameId()),
getStringById(p.getLuggage().getTextDescId())) + "\n");
ins.setIsLuggageShowed(true);
break;
case Constants.WORK_BTN:
dayNightFields.appendMessage(String.format(Constants.WORK_MESSAGE, callbackQuery.getFrom().getFirstName(),
getStringById(p.getWork().getTextNameId()),
getStringById(p.getWork().getTextDescId())) + "\n");
ins.setIsWorkShowed(true);
break;
}
setIsAnswered(callbackQuery.getFrom().getId());
updateInfoSections(p, ins);
sendApi(new SendMessage(callbackQuery.getMessage().getChatId()+"", Constants.THANK_YOU));
sendApi(new DeleteMessage(callbackQuery.getMessage().getChatId()+"", callbackQuery.getMessage().getMessageId()));
sendApi(new SendMessage(groupId, String.format(Constants.PRESSED_NIGHT, callbackQuery.getFrom().getFirstName())));
if( isAllAnswered() ) doDay();
}
}
private void sendApi(BotApiMethod<? extends Serializable> method) {
this.linkedQueue.add(method);
}
private void updateInfoSections(Player p1, InfoSections infoSections) {
players.stream().filter((p) -> p.equals(p1)).forEach((p) -> p.setInfoSections(infoSections));
}
private void setIsAnswered(Long id) {
players.stream().filter(p -> p.getTelegramId().equals(id) ).forEach(p -> p.setIsAnswered(true));
}
private void setIsVoted(Long id) {
players.stream().filter(p -> p.getTelegramId().equals(id) ).forEach(p -> p.setIsVoted(true));
}
private void doDay() {
dayNightFields.setIsNight(false);
double p = Math.floor(LiveFormula.calc(players, synergyService.getAllSynergies())*100d);
if( this.last_p < 0 ) { this.last_p = p; }
if( p > this.last_p ) {
sendApi(new SendMessage(groupId, String.format(Constants.DAY_MESSAGE_UPPER, (int) p, (p - this.last_p))));
} else if( p < this.last_p ) {
sendApi(new SendMessage(groupId, String.format(Constants.DAY_MESSAGE_DOWN, (int) p, (this.last_p - p))));
} else {
sendApi(new SendMessage(groupId, String.format(Constants.DAY_MESSAGE, p)));
}
this.last_p = p;
sendApi(new SendMessage(groupId, dayNightFields.getDayMessage()));
dayNightFields.setDayMessage("");
setAllNotAnswered();
setAllNotVoted();
SendPoll sp = new SendPoll(groupId, Constants.POLL_QUESTION, getAllUsers());
sp.setIsAnonymous(false);
sp.setAllowMultipleAnswers(false);
try {
dayNightFields.setLastPollId(sendApiMethod(sp).getPoll().getId());
} catch (TelegramApiException e) {
log.error(e.getMessage());
}
}
private void setAllNotVoted() {
players.forEach((p) -> p.setIsVoted(false));
}
private Boolean isAllVoted() {
return players.stream().allMatch(Player::getIsVoted);
}
private List<String> getAllUsers() {
return players.stream().map(Player::getFirstName).collect(Collectors.toList());
}
private void setAllNotAnswered() {
players.forEach((p) -> p.setIsAnswered(false));
}
private Boolean isAllAnswered() {
return players.stream().allMatch(Player::getIsAnswered);
}
private void showInfo() {
if( this.dayNightFields.getIsNight() ) {
sendApi(new SendMessage(groupId, Constants.CANT_SEND_NOT_DAY));
return;
}
String message = Constants.INFO_MESSAGE;
for( Player p : players ) {
message += p.getFirstName() + ":\n";
InfoSections s = p.getInfoSections();
if(s.getIsGenderShowed()) {
message += String.format(Constants.GENDER_MESAGE, p.getFirstName(), getStringById(p.getGender().getGenderTextId()),
p.getGender().getCanDie() ? Constants.TRUE : Constants.FALSE,
p.getGender().getIsMale() ? Constants.TRUE : Constants.FALSE,
p.getGender().getIsFemale() ? Constants.TRUE : Constants.FALSE) + "\n";
}
if(s.getIsAgeShowed()) {
message += String.format(Constants.AGE_MESSAGE, p.getFirstName(), p.getAge()) + "\n";
}
if(s.getIsLuggageShowed()) {
message += String.format(Constants.LUGG_MESSAGE, p.getFirstName(),
getStringById(p.getLuggage().getTextNameId()),
getStringById(p.getLuggage().getTextDescId())) + "\n";
}
if(s.getIsHealthShowed()) {
message += String.format(Constants.HEALTH_MESSAGE, p.getFirstName(), getStringById(p.getHealth().getTextNameId()),
getStringById(p.getHealth().getTextDescId()),
(int) (p.getHealth().getHealth_index()*100f),
p.getHealth().getIsChildfree() ? Constants.TRUE : Constants.FALSE) + "\n";
}
if(s.getIsWorkShowed()) {
message += String.format(Constants.WORK_MESSAGE, p.getFirstName(),
getStringById(p.getWork().getTextNameId()),
getStringById(p.getWork().getTextDescId())) + "\n";
}
if(s.getIsHobbyShowed()) {
message += String.format(Constants.HOBBY_MESSAGE, p.getFirstName(),
getStringById(p.getHobby().getTextDescId())) + "\n";
}
message += "\n";
}
sendApi(new SendMessage(groupId, message));
}
private void endVote() {
Integer max = dayNightFields.getPoll().values().stream().max(Integer::compareTo).get();
long count = dayNightFields.getPoll().values().stream().filter(p -> p.equals(max)).count();
SendMessage sendMessage = new SendMessage(groupId, Constants.ENDVOTE);
if( count > 1 ) {
sendMessage = new SendMessage(groupId, Constants.DRAW);
} else {
removeVotePlayers(max);
}
sendApi(sendMessage);
if( !checkEndGame() ) {
doNight();
return;
}
endGame();
}
private Boolean checkEndGame() {
return players.size() < 2 || dayNightFields.getTurnCount() >= 6;
}
private void endGame() {
double d = Math.floor(LiveFormula.calc(players, synergyService.getAllSynergies())*100d);
sendApi(new SendMessage(groupId, String.format(Constants.END_GAME, d)));
if(!players.isEmpty() && Math.floor(random.nextDouble()*100d) <= d) {
sendApi(new SendMessage(groupId, String.format(Constants.WIN_MESSAGE,
players.stream().map(Player::getFirstName).collect(Collectors.joining(", "))
)));
} else {
sendApi(new SendMessage(groupId, Constants.LOSE_MESSAGE));
}
interruptGame();
}
private void removeVotePlayers(Integer max) {
dayNightFields.getPoll()
.entrySet()
.stream()
.filter(e -> e.getValue().equals(max))
.forEach(i -> {
sendApi(new SendMessage(groupId, String.format(Constants.REMOVE_PLAYER, players.get(i.getKey()).getFirstName())));
players.remove(i.getKey().intValue());
});
}
private void interruptGame() {
this.dayNightFields = new DayNightFields();
this.players = new ArrayList<>();
this.gameState = GameState.NONE;
}
@Override
public void onUpdateReceived(@NotNull Update update) {
if( update.hasPollAnswer() && update.getPollAnswer().getPollId().equals(dayNightFields.getLastPollId())
&& !dayNightFields.getIsNight() && !update.getPollAnswer().getOptionIds().isEmpty()) {
if( getPlayerById(update.getPollAnswer().getUser().getId()).getIsVoted() )
return;
if( dayNightFields.getPoll().containsKey(update.getPollAnswer().getOptionIds().get(0)) ) {
dayNightFields.getPoll().put(update.getPollAnswer().getOptionIds().get(0),
dayNightFields.getPoll().get(update.getPollAnswer().getOptionIds().get(0)) + 1);
} else {
dayNightFields.getPoll().put(update.getPollAnswer().getOptionIds().get(0), 1);
}
setIsVoted(update.getPollAnswer().getUser().getId());
sendApi(new SendMessage(groupId, String.format(Constants.USER_VOTED, update.getPollAnswer().getUser().getFirstName())));
if( isAllVoted() )
endVote();
return;
}
if( update.hasCallbackQuery() ) {
sendApi(
new AnswerCallbackQuery(
update.getCallbackQuery().getId()
)
);
if( update.getCallbackQuery().getData().equals(HashUtils.bytesToHex(Constants.JOIN_GAME_BTN.getBytes())) ) {
joinGame(update.getCallbackQuery().getFrom(), 0);
} else if( update.getCallbackQuery().getData().equals(HashUtils.bytesToHex(Constants.START_GAME_BTN.getBytes())) ) {
if( isAdmin(update.getCallbackQuery().getFrom()) ) {
startGame();
return;
}
sendApi(new SendMessage(groupId, Constants.NOT_ADMIN_EXCEPTION));
} else if( gameState == GameState.STARTED && dayNightFields.getIsNight() ) {
processNightButton(update.getCallbackQuery());
}
return;
}
if( !(update.hasMessage() && update.getMessage().hasText() && update.getMessage().isCommand()) )
return;
String chatId = update.getMessage().getChatId()+"";
if( ( update.getMessage().getText().equals(Commands.SET_GROUP + "@" + getBotUsername()) ||
update.getMessage().getText().equals(Commands.SET_GROUP)) &&
update.getMessage().getFrom().getId().equals(getMasterId()) && gameState == GameState.NONE) {
groupId = chatId;
sendApi(new SendMessage(chatId, Constants.GROUP_SET));
}
if( !chatId.equals(groupId) )
return;
if ( (update.getMessage().getText().equals(Commands.START_GAME + "@" + getBotUsername()) ||
update.getMessage().getText().equals(Commands.START_GAME) ) &&
gameState == GameState.NONE) {
SendMessage message = new SendMessage(chatId, Constants.START_GAME_MSG);
message.setReplyMarkup(BotUtils.getJoinKeyboard());
sendApi(message);
gameState = GameState.JOINING;
} else if (update.getMessage().getText().equals(Commands.JOIN_GAME + "@" + getBotUsername()) ||
update.getMessage().getText().equals(Commands.JOIN_GAME)) {
joinGame(update.getMessage().getFrom(), update.getMessage().getMessageId());
} else if (update.getMessage().getText().equals(Commands.HELP + "@" + getBotUsername()) ||
update.getMessage().getText().equals(Commands.HELP)) {
sendApi(new SendMessage(chatId, Constants.HELP));
} else if ((update.getMessage().getText().equals(Commands.STOP_GAME + "@" + getBotUsername()) ||
update.getMessage().getText().equals(Commands.STOP_GAME) ) &&
gameState != GameState.NONE) {
if( isAdmin(update.getMessage().getFrom()) ) {
sendApi(new SendMessage(groupId, Constants.INTERRUPT_GAME));
interruptGame();
return;
}
sendApi(new SendMessage(groupId, Constants.NOT_ADMIN_EXCEPTION));
} else if((update.getMessage().getText().equals(Commands.GET_INFO + "@" + getBotUsername()) ||
update.getMessage().getText().equals(Commands.GET_INFO) ) &&
gameState == GameState.STARTED) {
showInfo();
}
}
private Player getPlayerById(Long id) {
return players.stream().filter((p) -> p.getTelegramId().equals(id) ).findFirst().orElse(null);
}
private Boolean hasPlayerWithId(Long id) {
return players.stream().anyMatch((p) -> p.getTelegramId().equals(id));
}
}

View File

@ -0,0 +1,75 @@
package com.alterdekim.javabot.controller;
import com.alterdekim.javabot.dto.UserDTO;
import com.alterdekim.javabot.entities.Invite;
import com.alterdekim.javabot.entities.User;
import com.alterdekim.javabot.service.InviteService;
import com.alterdekim.javabot.service.UserService;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
@Slf4j
@Controller
public class AuthController {
private final String base_title = " | Bunker";
private final UserService userService;
private final InviteService inviteService;
public AuthController(UserService userService, InviteService inviteService) {
this.inviteService = inviteService;
this.userService = userService;
}
@GetMapping("/login")
public String loginPage(Model model) {
model.addAttribute("title", "Login" + base_title);
return "login";
}
@GetMapping("/access-denied")
public String accessDenied(Model model) {
model.addAttribute("title", "Access denied");
return "access-denied";
}
@GetMapping("/signup")
public String showRegistrationForm(Model model) {
UserDTO userDto = new UserDTO();
model.addAttribute("user", userDto);
return "signup";
}
@PostMapping("/signup")
public String registration(
@ModelAttribute("user") @Valid UserDTO userDto,
BindingResult result,
Model model) {
User existingUser = userService.findByUsername(userDto.getUsername());
Invite existingInvite = inviteService.findById(1);
if(existingUser != null && existingUser.getUsername() != null && !existingUser.getUsername().isEmpty() ){
result.rejectValue("username", null,
"There is already an account registered with the same username");
return "redirect:/signup?error=1";
}
if(!existingInvite.getInvite_code().equals(userDto.getInvite_code())) {
result.rejectValue("invite_code", null, "Incorrect invite code.");
return "redirect:/signup?error=1";
}
if(result.hasErrors()) {
model.addAttribute("user", new UserDTO());
return "redirect:/signup?error=1";
}
userService.saveUser(userDto);
return "redirect:/";
}
}

View File

@ -0,0 +1,312 @@
package com.alterdekim.javabot.controller;
import com.alterdekim.javabot.dto.SynergyResult;
import com.alterdekim.javabot.entities.*;
import com.alterdekim.javabot.service.*;
import com.alterdekim.javabot.util.HashUtils;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Slf4j
@RestController
public class DatabaseController {
private final BioService bioService;
private final HealthService healthService;
private final HobbyService hobbyService;
private final LuggageService luggageService;
private final WorkService workService;
private final TextDataValService textDataValService;
private final DisasterService disasterService;
private final SynergyService synergyService;
public DatabaseController(
BioService bioService,
HealthService healthService,
HobbyService hobbyService,
LuggageService luggageService,
WorkService workService,
TextDataValService textDataValService,
DisasterService disasterService,
SynergyService synergyService) {
this.bioService = bioService;
this.healthService = healthService;
this.hobbyService = hobbyService;
this.luggageService = luggageService;
this.workService = workService;
this.textDataValService = textDataValService;
this.disasterService = disasterService;
this.synergyService = synergyService;
}
private void saveGender(Map<String, String> params) {
Boolean canDie = Boolean.parseBoolean(params.get("canDie"));
Boolean ismale = Boolean.parseBoolean(params.get("ismale"));
Boolean isfemale = Boolean.parseBoolean(params.get("isfemale"));
String gender_text = new String(HashUtils.decodeHexString(params.get("gender_text")));
TextDataVal t = textDataValService.save(new TextDataVal(gender_text));
bioService.saveBio(new Bio(ismale, isfemale, canDie, t.getId()));
}
private void saveHobby(Map<String, String> params) {
Float powerRange = Float.parseFloat(params.get("powerRange"));
Float violenceRange = Float.parseFloat(params.get("violenceRange"));
Float healRange = Float.parseFloat(params.get("healRange"));
Float foodRange = Float.parseFloat(params.get("foodRange"));
String hobby_text = new String(HashUtils.decodeHexString(params.get("hobby_text")));
TextDataVal t = textDataValService.save(new TextDataVal(hobby_text));
hobbyService.saveHobby(new Hobby(foodRange, powerRange, violenceRange, healRange, t.getId()));
}
private void saveLuggage(Map<String, String> params) {
Float powerRange = Float.parseFloat(params.get("powerRange"));
Float violenceRange = Float.parseFloat(params.get("violenceRange"));
Float healRange = Float.parseFloat(params.get("healRange"));
Float foodRange = Float.parseFloat(params.get("foodRange"));
Boolean isGarbage = Boolean.parseBoolean(params.get("isgarbage"));
String name_text = new String(HashUtils.decodeHexString(params.get("luggage_name_text")));
TextDataVal t1 = textDataValService.save(new TextDataVal(name_text));
String desc_text = new String(HashUtils.decodeHexString(params.get("luggage_desc_text")));
TextDataVal t2 = textDataValService.save(new TextDataVal(desc_text));
luggageService.saveLuggage(new Luggage(violenceRange, powerRange, healRange, foodRange, isGarbage, t1.getId(), t2.getId()));
}
private void saveHealth(Map<String, String> params) {
Float health_index = Float.parseFloat(params.get("health_index"));
Boolean childFree = Boolean.parseBoolean(params.get("childFree"));
String name_text = new String(HashUtils.decodeHexString(params.get("heal_name_text")));
TextDataVal t1 = textDataValService.save(new TextDataVal(name_text));
String desc_text = new String(HashUtils.decodeHexString(params.get("heal_desc_text")));
TextDataVal t2 = textDataValService.save(new TextDataVal(desc_text));
healthService.saveHealth(new Health(health_index, t1.getId(), t2.getId(), childFree));
}
private void saveWork(Map<String, String> params) {
Float powerRange = Float.parseFloat(params.get("powerRange"));
Float violenceRange = Float.parseFloat(params.get("violenceRange"));
Float healRange = Float.parseFloat(params.get("healRange"));
Float foodRange = Float.parseFloat(params.get("foodRange"));
String name_text = new String(HashUtils.decodeHexString(params.get("work_name_text")));
TextDataVal t1 = textDataValService.save(new TextDataVal(name_text));
String desc_text = new String(HashUtils.decodeHexString(params.get("work_desc_text")));
TextDataVal t2 = textDataValService.save(new TextDataVal(desc_text));
workService.saveWork(new Work(healRange, powerRange, violenceRange, foodRange, t1.getId(), t2.getId()));
}
private void saveDiss(Map<String, String> params) {
String name_text = new String(HashUtils.decodeHexString(params.get("diss_name_text")));
TextDataVal t1 = textDataValService.save(new TextDataVal(name_text));
String desc_text = new String(HashUtils.decodeHexString(params.get("diss_desc_text")));
TextDataVal t2 = textDataValService.save(new TextDataVal(desc_text));
disasterService.saveDisaster(new Disaster(t1.getId(), t2.getId()));
}
@PostMapping("/api/remove_synergy")
public String remove_synergy(@RequestParam Map<String, String> params) {
Long id = Long.parseLong(params.get("synergy_id"));
synergyService.removeById(id);
return "ok";
}
@PostMapping("/api/add_synergy")
public String add_synergy(@RequestParam Map<String, String> params) {
Long feid = Long.parseLong(params.get("first_entity_id"));
ColumnType fetype = ColumnType.values()[Integer.parseInt(params.get("first_entity_type"))];
Long seid = Long.parseLong(params.get("second_entity_id"));
ColumnType setype = ColumnType.values()[Integer.parseInt(params.get("second_entity_type"))];
Float probability = Float.parseFloat(params.get("probability"));
synergyService.saveSynergy(new Synergy(feid, fetype, seid, setype, probability));
return "ok";
}
@PostMapping("/api/get_synergies")
public String get_synergies(@RequestParam Map<String, String> params) {
Long id = Long.parseLong(params.get("entity_id"));
String section = params.get("entity_type");
ObjectMapper mapper = new ObjectMapper();
try {
List<Synergy> synergyList = new ArrayList<>();
switch (section) {
case "agge":
synergyList = bioService.getSynergies(id);
break;
case "lugg":
synergyList = luggageService.getSynergies(id);
break;
case "prof":
synergyList = workService.getSynergies(id);
break;
case "heal":
synergyList = healthService.getSynergies(id);
break;
case "hobb":
synergyList = hobbyService.getSynergies(id);
break;
}
List<SynergyResult> results = new ArrayList<>();
for( Synergy s : synergyList ) {
String textFirst = getText(s.getFirstType(), s.getFirstEntityId());
String textSecond = getText(s.getSecondType(), s.getSecondEntityId());
results.add(new SynergyResult(s.getId(), textFirst, textSecond, s.getFirstType(), s.getSecondType(), s.getProbabilityValue()));
}
return mapper.writeValueAsString(results);
} catch (JacksonException e) {
log.error(e.getMessage());
}
return "ok";
}
private String getText(ColumnType type, Long feid) {
switch (type) {
case Bio:
return textDataValService.getTextDataValById(bioService.getBioById(feid).getGenderTextId()).getText();
case Health:
return textDataValService.getTextDataValById(healthService.getHealthById(feid).getTextNameId()).getText();
case Hobby:
return textDataValService.getTextDataValById(hobbyService.getHobbyById(feid).getTextDescId()).getText();
case Luggage:
return textDataValService.getTextDataValById(luggageService.getLuggageById(feid).getTextNameId()).getText();
case Work:
return textDataValService.getTextDataValById(workService.getWorkById(feid).getTextNameId()).getText();
}
return "-";
}
@PostMapping("/api/add_entry")
public String add_entry(@RequestParam Map<String, String> params) {
/* additional data, disasters */
String section = params.get("section");
switch (section) {
case "agge":
saveGender(params);
break;
case "lugg":
saveLuggage(params);
break;
case "prof":
saveWork(params);
break;
case "heal":
saveHealth(params);
break;
case "hobb":
saveHobby(params);
break;
case "diss":
saveDiss(params);
break;
default:
saveDiss(params);
break;
}
return "ok";
}
@PostMapping("/api/remove_entry")
public String remove_entry(@RequestParam Map<String, String> params) {
String section = params.get("section");
long entry_id = Long.parseLong(params.get("entry_id"));
switch (section) {
case "agge":
bioService.removeById(entry_id);
break;
case "hobb":
hobbyService.removeById(entry_id);
break;
case "lugg":
luggageService.removeById(entry_id);
break;
case "heal":
healthService.removeById(entry_id);
break;
case "prof":
workService.removeById(entry_id);
break;
case "diss":
disasterService.removeById(entry_id);
break;
default:
disasterService.removeById(entry_id);
break;
}
return "ok";
}
@PostMapping("/api/getTextById")
public String getText(@RequestParam Map<String, String> params) {
long l = Long.parseLong(params.get("entry_id"));
return textDataValService.getTextDataValById(l).getText();
}
@PostMapping("/api/get_entries")
public String getEntries(@RequestParam Map<String, String> params) {
ObjectMapper mapper = new ObjectMapper();
try {
switch (params.get("section")) {
case "agge":
return mapper.writeValueAsString(bioService.getAllBios());
case "hobb":
return mapper.writeValueAsString(hobbyService.getAllHobbies());
case "prof":
return mapper.writeValueAsString(workService.getAllWorks());
case "heal":
return mapper.writeValueAsString(healthService.getAllHealth());
case "lugg":
return mapper.writeValueAsString(luggageService.getAllLuggages());
case "diss":
return mapper.writeValueAsString(disasterService.getAllDisasters());
default:
return mapper.writeValueAsString(disasterService.getAllDisasters());
}
} catch (JacksonException e) {
log.error(e.getMessage());
}
return "error";
}
@PostMapping("/api/edit_entry")
public String edit_entry(@RequestParam Map<String, String> params) {
ObjectMapper mapper = new ObjectMapper();
long l = Long.parseLong(params.get("entry_id"));
try {
switch (params.get("section")) {
case "agge":
return mapper.writeValueAsString(bioService.getBioById(l));
case "hobb":
return mapper.writeValueAsString(hobbyService.getHobbyById(l));
case "prof":
return mapper.writeValueAsString(workService.getWorkById(l));
case "heal":
return mapper.writeValueAsString(healthService.getHealthById(l));
case "lugg":
return mapper.writeValueAsString(luggageService.getLuggageById(l));
case "diss":
return mapper.writeValueAsString(disasterService.getDisasterById(l));
default:
return mapper.writeValueAsString(disasterService.getDisasterById(l));
}
} catch (JacksonException e) {
log.error(e.getMessage());
}
return "error";
}
}

View File

@ -0,0 +1,175 @@
package com.alterdekim.javabot.controller;
import com.alterdekim.javabot.entities.*;
import com.alterdekim.javabot.service.*;
import com.alterdekim.javabot.util.UAgentInfo;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import java.io.Serializable;
import java.util.*;
@Slf4j
@Controller
public class PanelController {
private final BioService bioService;
private final HealthService healthService;
private final HobbyService hobbyService;
private final LuggageService luggageService;
private final WorkService workService;
private final TextDataValService textDataValService;
private final DisasterService disasterService;
public PanelController(
BioService bioService,
HealthService healthService,
HobbyService hobbyService,
LuggageService luggageService,
WorkService workService,
TextDataValService textDataValService,
DisasterService disasterService) {
this.bioService = bioService;
this.healthService = healthService;
this.hobbyService = hobbyService;
this.luggageService = luggageService;
this.workService = workService;
this.textDataValService = textDataValService;
this.disasterService = disasterService;
}
private List<Card> dissToCards() {
List<Disaster> bios = disasterService.getAllDisasters();
List<Card> cards = new ArrayList<>();
for( Disaster b : bios ) {
Card card = new Card();
card.setId(b.getId());
card.setTitle(textDataValService.getTextDataValById(b.getNameTextId()).getText());
card.setBody(Collections.singletonList("Description: " + textDataValService.getTextDataValById(b.getDescTextId()).getText()));
cards.add(card);
}
cards.sort(Comparator.comparing(Card::getId));
Collections.reverse(cards);
return cards;
}
private List<Card> bioToCards() {
List<Bio> bios = bioService.getAllBios();
List<Card> cards = new ArrayList<>();
for( Bio b : bios ) {
Card card = new Card();
card.setId(b.getId());
card.setTitle(textDataValService.getTextDataValById(b.getGenderTextId()).getText());
card.setBody(Arrays.asList("canDie: " + b.getCanDie(), "isMale: " + b.getIsMale(), "isFemale: " + b.getIsFemale()));
cards.add(card);
}
cards.sort(Comparator.comparing(Card::getId));
Collections.reverse(cards);
return cards;
}
private List<Card> hobbyToCards() {
List<Hobby> bios = hobbyService.getAllHobbies();
List<Card> cards = new ArrayList<>();
for( Hobby b : bios ) {
Card card = new Card();
card.setId(b.getId());
card.setTitle(textDataValService.getTextDataValById(b.getTextDescId()).getText());
card.setBody(Arrays.asList("Foodstuffs: " + b.getFoodstuffs(), "Asocial: " + b.getAsocial(), "Power: " + b.getPower(), "Violence:" + b.getViolence()));
cards.add(card);
}
cards.sort(Comparator.comparing(Card::getId));
Collections.reverse(cards);
return cards;
}
private List<Card> workToCards() {
List<Work> bios = workService.getAllWorks();
List<Card> cards = new ArrayList<>();
for( Work b : bios ) {
Card card = new Card();
card.setId(b.getId());
card.setTitle(textDataValService.getTextDataValById(b.getTextNameId()).getText());
card.setBody(Arrays.asList("Foodstuffs: " + b.getFoodstuffs(), "Power: " + b.getPower(), "Violence: " + b.getViolence(), "Asocial: " + b.getAsocial(), "Description: " + textDataValService.getTextDataValById(b.getTextDescId()).getText()));
cards.add(card);
}
cards.sort(Comparator.comparing(Card::getId));
Collections.reverse(cards);
return cards;
}
private List<Card> healToCards() {
List<Health> bios = healthService.getAllHealth();
List<Card> cards = new ArrayList<>();
for( Health b : bios ) {
Card card = new Card();
card.setId(b.getId());
card.setTitle(textDataValService.getTextDataValById(b.getTextNameId()).getText());
card.setBody(Arrays.asList("Health index: " + b.getHealth_index(),"isChildfree: " + b.getIsChildfree(), "Description: " + textDataValService.getTextDataValById(b.getTextDescId()).getText()));
cards.add(card);
}
cards.sort(Comparator.comparing(Card::getId));
Collections.reverse(cards);
return cards;
}
private List<Card> luggageToCards() {
List<Luggage> bios = luggageService.getAllLuggages();
List<Card> cards = new ArrayList<>();
for( Luggage b : bios ) {
Card card = new Card();
card.setId(b.getId());
card.setTitle(textDataValService.getTextDataValById(b.getTextNameId()).getText());
card.setBody(Arrays.asList("Foodstuffs: " + b.getFoodstuffs(), "Power: " + b.getPower(), "Asocial: " + b.getAsocial(), "Violence: " + b.getViolence(), "Is garbage: " + b.getGarbage(), "Description: " + textDataValService.getTextDataValById(b.getTextDescId()).getText()));
cards.add(card);
}
cards.sort(Comparator.comparing(Card::getId));
Collections.reverse(cards);
return cards;
}
@GetMapping("/panel")
public String panelPage(Model model, @RequestHeader("User-Agent") String uagent, @RequestHeader("Accept") String accepth, @RequestParam(value = "section", defaultValue = "diss") String section) {
model.addAttribute("is_mobile", new UAgentInfo(uagent, accepth).detectSmartphone());
model.addAttribute("section", section);
switch (section) {
case "lugg":
model.addAttribute( "cards", luggageToCards() );
break;
case "heal":
model.addAttribute("cards", healToCards() );
break;
case "prof":
model.addAttribute("cards", workToCards() );
break;
case "hobb":
model.addAttribute("cards", hobbyToCards() );
break;
case "agge":
model.addAttribute("cards", bioToCards() );
break;
case "diss":
model.addAttribute("cards", dissToCards() );
break;
case "stats":
// !
break;
}
return "panel";
}
@Getter
@Setter
@NoArgsConstructor
private static class Card implements Serializable {
private String title;
private List<String> body;
private Long id;
}
}

View File

@ -0,0 +1,18 @@
package com.alterdekim.javabot.dto;
import com.alterdekim.javabot.entities.ColumnType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class SynergyResult {
private Long id;
private String firstEntityText;
private String secondEntityText;
private ColumnType firstType;
private ColumnType secondType;
private Float probabilityValue;
}

View File

@ -0,0 +1,23 @@
package com.alterdekim.javabot.dto;
import jakarta.validation.constraints.NotEmpty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class UserDTO {
private Long id;
@NotEmpty(message = "Username should not be empty")
private String username;
@NotEmpty(message = "Password should not be empty")
private String password;
@NotEmpty(message = "Invite code should not be empty")
private String invite_code;
private String lang;
}

View File

@ -0,0 +1,37 @@
package com.alterdekim.javabot.entities;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "addfacts")
public class AdditionalFacts {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable=false)
private Float violence;
@Column(nullable=false)
private Float power;
@Column(nullable=false)
private Float food;
@Column(nullable=false)
private Float heal;
@Column(nullable = false)
private Float health_index;
@Column(nullable = false)
private Long textDescId;
}

View File

@ -0,0 +1,42 @@
package com.alterdekim.javabot.entities;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.JoinFormula;
import java.util.List;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "bio")
public class Bio {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public Bio(Boolean isMale, Boolean isFemale, Boolean canDie, Long genderTextId) {
this.isMale = isMale;
this.isFemale = isFemale;
this.canDie = canDie;
this.genderTextId = genderTextId;
}
@Column(nullable = false)
private Boolean isMale;
@Column(nullable = false)
private Boolean isFemale;
@Column(nullable = false)
private Boolean canDie;
@Column(nullable = false)
private Long genderTextId;
}

View File

@ -0,0 +1,9 @@
package com.alterdekim.javabot.entities;
public enum ColumnType {
Bio,
Health,
Hobby,
Luggage,
Work
}

View File

@ -0,0 +1,31 @@
package com.alterdekim.javabot.entities;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "disasters")
public class Disaster {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private Long nameTextId;
@Column(nullable = false)
private Long descTextId;
public Disaster(Long nameTextId, Long descTextId) {
this.nameTextId = nameTextId;
this.descTextId = descTextId;
}
}

View File

@ -0,0 +1,38 @@
package com.alterdekim.javabot.entities;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "game_stats")
public class GameStats {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private Long gameId;
@Column(nullable = false)
private Short turnNumber;
@Column(nullable = false)
private Double probability;
@Column(nullable = false)
private Integer playersCount;
public GameStats(Long gameId, Short turnNumber, Double probability, Integer playersCount) {
this.gameId = gameId;
this.turnNumber = turnNumber;
this.probability = probability;
this.playersCount = playersCount;
}
}

View File

@ -0,0 +1,39 @@
package com.alterdekim.javabot.entities;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "games_list")
public class Games {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private Boolean hasEnded;
@Column(nullable = false)
private Boolean hasStoppedManually;
@Column(nullable = false)
private Boolean isWon;
@Column(nullable = false)
private Long started;
public Games(Boolean hasEnded, Boolean hasStoppedManually, Boolean isWon, Long started) {
this.hasEnded = hasEnded;
this.hasStoppedManually = hasStoppedManually;
this.isWon = isWon;
this.started = started;
}
}

View File

@ -0,0 +1,43 @@
package com.alterdekim.javabot.entities;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.JoinFormula;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
import java.util.Set;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "health")
public class Health {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private Float health_index;
@Column(nullable = false)
private Long textNameId;
@Column(nullable = false)
private Long textDescId;
@Column(nullable = false)
private Boolean isChildfree;
public Health(Float health_index, Long textNameId, Long textDescId, Boolean isChildfree) {
this.health_index = health_index;
this.textNameId = textNameId;
this.textDescId = textDescId;
this.isChildfree = isChildfree;
}
}

View File

@ -0,0 +1,51 @@
package com.alterdekim.javabot.entities;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.JoinFormula;
import java.util.List;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "hobby")
public class Hobby {
public Hobby(Float foodstuffs, Float power, Float violence, Float asocial, Long textDescId) {
this.foodstuffs = foodstuffs;
this.power = power;
this.violence = violence;
this.asocial = asocial;
this.textDescId = textDescId;
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private Float asocial;
@Column(nullable=false)
private Float power;
@Column(nullable=false)
private Float violence;
@Column(nullable=false)
private Float foodstuffs;
@Column(nullable = false)
private Long textDescId;
public Double getValue() {
return ((this.getFoodstuffs().doubleValue() +
this.getPower().doubleValue()) / 2.0d) - (((this.getViolence().doubleValue() + this.getAsocial().doubleValue()) / 2.0d));
}
}

View File

@ -0,0 +1,22 @@
package com.alterdekim.javabot.entities;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "invite_codes")
public class Invite {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable=false)
private String invite_code;
}

View File

@ -0,0 +1,61 @@
package com.alterdekim.javabot.entities;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.JoinColumnOrFormula;
import org.hibernate.annotations.JoinColumnsOrFormulas;
import org.hibernate.annotations.JoinFormula;
import org.hibernate.annotations.WhereJoinTable;
import java.util.List;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "luggage")
public class Luggage {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public Luggage(Float violence, Float power, Float asocial, Float foodstuffs, Boolean garbage, Long textNameId, Long textDescId) {
this.violence = violence;
this.power = power;
this.asocial = asocial;
this.foodstuffs = foodstuffs;
this.garbage = garbage;
this.textNameId = textNameId;
this.textDescId = textDescId;
}
@Column(nullable=false)
private Float violence;
@Column(nullable=false)
private Float power;
@Column(nullable = false)
private Float asocial;
@Column(nullable=false)
private Float foodstuffs;
@Column(nullable=false)
private Boolean garbage;
@Column(nullable = false)
private Long textNameId;
@Column(nullable = false)
private Long textDescId;
public Double getValue() {
return ((this.getFoodstuffs().doubleValue() +
this.getPower().doubleValue()) * 0.5d) - (((this.getViolence().doubleValue() + this.getAsocial().doubleValue()) / 2.0d));
}
}

View File

@ -0,0 +1,28 @@
package com.alterdekim.javabot.entities;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.List;
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name="roles")
public class Role
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable=false, unique=true)
private String name;
@ManyToMany(mappedBy="roles")
private List<User> users;
}

View File

@ -0,0 +1,42 @@
package com.alterdekim.javabot.entities;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "synergy")
public class Synergy {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private Long firstEntityId;
@Enumerated(EnumType.ORDINAL)
private ColumnType firstType;
@Column(nullable = false)
private Long secondEntityId;
@Enumerated(EnumType.ORDINAL)
private ColumnType secondType;
@Column(nullable = false)
private Float probabilityValue;
public Synergy(Long firstEntityId, ColumnType firstType, Long secondEntityId, ColumnType secondType, Float probabilityValue) {
this.firstEntityId = firstEntityId;
this.firstType = firstType;
this.secondEntityId = secondEntityId;
this.secondType = secondType;
this.probabilityValue = probabilityValue;
}
}

View File

@ -0,0 +1,28 @@
package com.alterdekim.javabot.entities;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "textdata_val")
public class TextDataVal {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(length = 65555)
@NotNull
private String text;
public TextDataVal(String text) {
this.text = text;
}
}

View File

@ -0,0 +1,39 @@
package com.alterdekim.javabot.entities;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name="users")
public class User {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable=false, unique=true)
private String username;
@Column(nullable=false)
private String password;
@ManyToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
@JoinTable(
name="users_roles",
joinColumns={@JoinColumn(name="USER_ID", referencedColumnName="ID")},
inverseJoinColumns={@JoinColumn(name="ROLE_ID", referencedColumnName="ID")})
private List<Role> roles = new ArrayList<>();
}

View File

@ -0,0 +1,55 @@
package com.alterdekim.javabot.entities;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.JoinFormula;
import java.util.List;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "work")
public class Work {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable=false)
private Float asocial;
@Column(nullable=false)
private Float power;
@Column(nullable=false)
private Float violence;
@Column(nullable=false)
private Float foodstuffs;
@Column(nullable = false)
private Long textNameId;
@Column(nullable = false)
private Long textDescId;
public Work(Float asocial, Float power, Float violence, Float foodstuffs, Long textNameId, Long textDescId) {
this.asocial = asocial;
this.power = power;
this.violence = violence;
this.foodstuffs = foodstuffs;
this.textNameId = textNameId;
this.textDescId = textDescId;
}
public Double getValue() {
return ((this.getFoodstuffs().doubleValue() +
this.getPower().doubleValue()) * 0.5d) - (((this.getViolence().doubleValue() + this.getAsocial().doubleValue()) / 2.0d));
}
}

View File

@ -0,0 +1,15 @@
package com.alterdekim.javabot.handler;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import java.io.IOException;
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
response.sendRedirect("/access-denied");
}
}

View File

@ -0,0 +1,13 @@
package com.alterdekim.javabot.repository;
import com.alterdekim.javabot.entities.AdditionalFacts;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface AdditionalFactsRepository extends JpaRepository<AdditionalFacts, Long> {
Optional<AdditionalFacts> findById(Long id);
List<AdditionalFacts> findAll();
}

View File

@ -0,0 +1,19 @@
package com.alterdekim.javabot.repository;
import com.alterdekim.javabot.entities.Bio;
import com.alterdekim.javabot.entities.Synergy;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;
public interface BioRepository extends JpaRepository<Bio, Long> {
Optional<Bio> findById(Long id);
List<Bio> findAll();
@Query(value = "SELECT new Synergy(s.id, s.firstEntityId, s.firstType, s.secondEntityId, s.secondType, s.probabilityValue) FROM Synergy s WHERE (s.firstType = 0 AND s.firstEntityId = :uuid) OR (s.secondType = 0 AND s.secondEntityId = :uuid)")
List<Synergy> getSynergies(@Param(value = "uuid") Long bioId);
}

View File

@ -0,0 +1,13 @@
package com.alterdekim.javabot.repository;
import com.alterdekim.javabot.entities.Disaster;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface DisasterRepository extends JpaRepository<Disaster, Long> {
Optional<Disaster> findById(Long id);
List<Disaster> findAll();
}

View File

@ -0,0 +1,19 @@
package com.alterdekim.javabot.repository;
import com.alterdekim.javabot.entities.Health;
import com.alterdekim.javabot.entities.Synergy;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;
public interface HealthRepository extends JpaRepository<Health, Long> {
Optional<Health> findById(Long id);
List<Health> findAll();
@Query(value = "SELECT new Synergy(s.id, s.firstEntityId, s.firstType, s.secondEntityId, s.secondType, s.probabilityValue) FROM Synergy s WHERE (s.firstType = 1 AND s.firstEntityId = :uuid) OR (s.secondType = 1 AND s.secondEntityId = :uuid)")
List<Synergy> getSynergies(@Param(value = "uuid") Long healthId);
}

View File

@ -0,0 +1,19 @@
package com.alterdekim.javabot.repository;
import com.alterdekim.javabot.entities.Hobby;
import com.alterdekim.javabot.entities.Synergy;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;
public interface HobbyRepository extends JpaRepository<Hobby, Long> {
Optional<Hobby> findById(Long id);
List<Hobby> findAll();
@Query(value = "SELECT new Synergy(s.id, s.firstEntityId, s.firstType, s.secondEntityId, s.secondType, s.probabilityValue) FROM Synergy s WHERE (s.firstType = 2 AND s.firstEntityId = :uuid) OR (s.secondType = 2 AND s.secondEntityId = :uuid)")
List<Synergy> getSynergies(@Param(value = "uuid") Long hobbId);
}

View File

@ -0,0 +1,10 @@
package com.alterdekim.javabot.repository;
import com.alterdekim.javabot.entities.Invite;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface InviteRepository extends JpaRepository<Invite, Integer> {
Optional<Invite> findById(Integer id);
}

View File

@ -0,0 +1,19 @@
package com.alterdekim.javabot.repository;
import com.alterdekim.javabot.entities.Luggage;
import com.alterdekim.javabot.entities.Synergy;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;
public interface LuggageRepository extends JpaRepository<Luggage, Long> {
Optional<Luggage> findById(Long id);
List<Luggage> findAll();
@Query(value = "SELECT new Synergy(s.id, s.firstEntityId, s.firstType, s.secondEntityId, s.secondType, s.probabilityValue) FROM Synergy s WHERE (s.firstType = 3 AND s.firstEntityId = :uuid) OR (s.secondType = 3 AND s.secondEntityId = :uuid)")
List<Synergy> getSynergies(@Param(value = "uuid") Long luggId);
}

View File

@ -0,0 +1,12 @@
package com.alterdekim.javabot.repository;
import com.alterdekim.javabot.entities.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface RoleRepository extends JpaRepository<Role, Long> {
Role findByName(String name);
List<Role> findAll();
}

View File

@ -0,0 +1,13 @@
package com.alterdekim.javabot.repository;
import com.alterdekim.javabot.entities.Synergy;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface SynergyRepository extends JpaRepository<Synergy, Long> {
Optional<Synergy> findById(Long id);
List<Synergy> findAll();
}

View File

@ -0,0 +1,10 @@
package com.alterdekim.javabot.repository;
import com.alterdekim.javabot.entities.TextDataVal;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface TextDataValRepository extends JpaRepository<TextDataVal, Long> {
Optional<TextDataVal> findById(Long id);
}

View File

@ -0,0 +1,11 @@
package com.alterdekim.javabot.repository;
import com.alterdekim.javabot.entities.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
User findByUsername(String username);
}

View File

@ -0,0 +1,19 @@
package com.alterdekim.javabot.repository;
import com.alterdekim.javabot.entities.Synergy;
import com.alterdekim.javabot.entities.Work;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;
public interface WorkRepository extends JpaRepository<Work, Long> {
Optional<Work> findById(Long id);
List<Work> findAll();
@Query(value = "SELECT new Synergy(s.id, s.firstEntityId, s.firstType, s.secondEntityId, s.secondType, s.probabilityValue) FROM Synergy s WHERE (s.firstType = 4 AND s.firstEntityId = :uuid) OR (s.secondType = 4 AND s.secondEntityId = :uuid)")
List<Synergy> getSynergies(@Param(value = "uuid") Long workId);
}

View File

@ -0,0 +1,77 @@
package com.alterdekim.javabot.security;
import com.alterdekim.javabot.handler.CustomAccessDeniedHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
@EnableWebSecurity
public class SpringSecurity {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
requestCache.setMatchingRequestParameterName(null);
http.csrf().disable()
.authorizeHttpRequests((authorize) ->
authorize
.requestMatchers("/panel").hasAnyAuthority("ROLE_ADMIN")
.requestMatchers("/api/**").hasAnyAuthority("ROLE_ADMIN")
.requestMatchers("/static/**").permitAll()
.requestMatchers("/access-denied").permitAll()
.requestMatchers("/signup").permitAll()
.requestMatchers("/favicon.ico").permitAll()
.requestMatchers("/signup/**").permitAll()
.requestMatchers("/").permitAll()
).formLogin(
form -> form
.loginPage("/login")
.loginProcessingUrl("/login")
.failureForwardUrl("/")
.defaultSuccessUrl("/panel")
.permitAll()
)
.logout(
logout -> logout
.logoutUrl("/logout")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.permitAll()
)
.requestCache((cache) -> cache
.requestCache(requestCache)
)
.exceptionHandling((exc) -> exc
.accessDeniedHandler(accessDeniedHandler())
.accessDeniedPage("/access-denied"));
return http.build();
}
@Bean
public AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public static PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}

View File

@ -0,0 +1,10 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.AdditionalFacts;
import java.util.List;
public interface AdditionalFactsService {
List<AdditionalFacts> getAllFacts();
AdditionalFacts getAdditionalFactById(long addFactId);
}

View File

@ -0,0 +1,27 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.AdditionalFacts;
import com.alterdekim.javabot.repository.AdditionalFactsRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AdditionalFactsServiceImpl implements AdditionalFactsService {
private AdditionalFactsRepository repository;
public AdditionalFactsServiceImpl(AdditionalFactsRepository repository) {
this.repository = repository;
}
@Override
public List<AdditionalFacts> getAllFacts() {
return repository.findAll();
}
@Override
public AdditionalFacts getAdditionalFactById(long addFactId) {
return repository.findById(addFactId).orElse(null);
}
}

View File

@ -0,0 +1,17 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Bio;
import com.alterdekim.javabot.entities.Synergy;
import java.util.List;
public interface BioService {
List<Bio> getAllBios();
Bio getBioById(long bioId);
void saveBio(Bio bio);
void removeById(Long id);
List<Synergy> getSynergies(Long id);
}

View File

@ -0,0 +1,43 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Bio;
import com.alterdekim.javabot.entities.Synergy;
import com.alterdekim.javabot.repository.BioRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BioServiceImpl implements BioService {
private BioRepository repository;
public BioServiceImpl(BioRepository repository) {
this.repository = repository;
}
@Override
public List<Bio> getAllBios() {
return repository.findAll();
}
@Override
public Bio getBioById(long bioId) {
return repository.findById(bioId).orElse(null);
}
@Override
public void saveBio(Bio bio) {
repository.save(bio);
}
@Override
public void removeById(Long id) {
repository.deleteById(id);
}
@Override
public List<Synergy> getSynergies(Long id) {
return repository.getSynergies(id);
}
}

View File

@ -0,0 +1,43 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Role;
import com.alterdekim.javabot.entities.User;
import com.alterdekim.javabot.repository.UserRepository;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.stream.Collectors;
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByUsername(email);
if (user != null) {
return new org.springframework.security.core.userdetails.User(user.getUsername(),
user.getPassword(),
mapRolesToAuthorities(user.getRoles()));
}else{
throw new UsernameNotFoundException("Invalid username or password.");
}
}
private Collection < ? extends GrantedAuthority> mapRolesToAuthorities(Collection <Role> roles) {
return roles.stream()
.map(role -> new SimpleGrantedAuthority(role.getName()))
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,14 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Disaster;
import java.util.List;
public interface DisasterService {
List<Disaster> getAllDisasters();
Disaster getDisasterById(long dId);
void removeById(long dId);
void saveDisaster(Disaster disaster);
}

View File

@ -0,0 +1,37 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Disaster;
import com.alterdekim.javabot.repository.DisasterRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class DisasterServiceImpl implements DisasterService {
private final DisasterRepository repository;
public DisasterServiceImpl(DisasterRepository repository) {
this.repository = repository;
}
@Override
public List<Disaster> getAllDisasters() {
return repository.findAll();
}
@Override
public Disaster getDisasterById(long dId) {
return repository.findById(dId).orElse(null);
}
@Override
public void removeById(long dId) {
repository.deleteById(dId);
}
@Override
public void saveDisaster(Disaster disaster) {
repository.save(disaster);
}
}

View File

@ -0,0 +1,17 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Health;
import com.alterdekim.javabot.entities.Synergy;
import java.util.List;
public interface HealthService {
List<Health> getAllHealth();
Health getHealthById(long healthId);
void removeById(long healthId);
void saveHealth(Health health);
List<Synergy> getSynergies(Long id);
}

View File

@ -0,0 +1,43 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Health;
import com.alterdekim.javabot.entities.Synergy;
import com.alterdekim.javabot.repository.HealthRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class HealthServiceImpl implements HealthService {
private final HealthRepository repository;
public HealthServiceImpl(HealthRepository repository) {
this.repository = repository;
}
@Override
public List<Health> getAllHealth() {
return repository.findAll();
}
@Override
public Health getHealthById(long healthId) {
return repository.findById(healthId).orElse(null);
}
@Override
public void removeById(long healthId) {
repository.deleteById(healthId);
}
@Override
public void saveHealth(Health health) {
repository.save(health);
}
@Override
public List<Synergy> getSynergies(Long id) {
return repository.getSynergies(id);
}
}

View File

@ -0,0 +1,17 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Hobby;
import com.alterdekim.javabot.entities.Synergy;
import java.util.List;
public interface HobbyService {
List<Hobby> getAllHobbies();
Hobby getHobbyById(long hobbyId);
void saveHobby(Hobby hobby);
void removeById(Long id);
List<Synergy> getSynergies(Long id);
}

View File

@ -0,0 +1,43 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Hobby;
import com.alterdekim.javabot.entities.Synergy;
import com.alterdekim.javabot.repository.HobbyRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class HobbyServiceImpl implements HobbyService {
private final HobbyRepository repository;
public HobbyServiceImpl(HobbyRepository repository) {
this.repository = repository;
}
@Override
public List<Hobby> getAllHobbies() {
return repository.findAll();
}
@Override
public Hobby getHobbyById(long hobbyId) {
return repository.findById(hobbyId).orElse(null);
}
@Override
public void saveHobby(Hobby hobby) {
repository.save(hobby);
}
@Override
public void removeById(Long id) {
repository.deleteById(id);
}
@Override
public List<Synergy> getSynergies(Long id) {
return repository.getSynergies(id);
}
}

View File

@ -0,0 +1,7 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Invite;
public interface InviteService {
Invite findById(Integer id);
}

View File

@ -0,0 +1,20 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Invite;
import com.alterdekim.javabot.repository.InviteRepository;
import org.springframework.stereotype.Service;
@Service
public class InviteServiceImpl implements InviteService {
private final InviteRepository repository;
public InviteServiceImpl(InviteRepository repository) {
this.repository = repository;
}
@Override
public Invite findById(Integer id) {
return repository.findById(id).orElse(null);
}
}

View File

@ -0,0 +1,17 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Luggage;
import com.alterdekim.javabot.entities.Synergy;
import java.util.List;
public interface LuggageService {
List<Luggage> getAllLuggages();
Luggage getLuggageById(long luggageId);
void removeById(long luggageId);
void saveLuggage(Luggage luggage);
List<Synergy> getSynergies(Long id);
}

View File

@ -0,0 +1,43 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Luggage;
import com.alterdekim.javabot.entities.Synergy;
import com.alterdekim.javabot.repository.LuggageRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class LuggageServiceImpl implements LuggageService {
private final LuggageRepository repository;
public LuggageServiceImpl(LuggageRepository repository) {
this.repository = repository;
}
@Override
public List<Luggage> getAllLuggages() {
return repository.findAll();
}
@Override
public Luggage getLuggageById(long luggageId) {
return repository.findById(luggageId).orElse(null);
}
@Override
public void removeById(long luggageId) {
repository.deleteById(luggageId);
}
@Override
public void saveLuggage(Luggage luggage) {
repository.save(luggage);
}
@Override
public List<Synergy> getSynergies(Long id) {
return repository.getSynergies(id);
}
}

View File

@ -0,0 +1,14 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Luggage;
import com.alterdekim.javabot.entities.Synergy;
import java.util.List;
public interface SynergyService {
void removeById(long synergyId);
void saveSynergy(Synergy synergy);
List<Synergy> getAllSynergies();
}

View File

@ -0,0 +1,32 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Synergy;
import com.alterdekim.javabot.repository.SynergyRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class SynergyServiceImpl implements SynergyService {
private final SynergyRepository repository;
public SynergyServiceImpl(SynergyRepository repository) {
this.repository = repository;
}
@Override
public void removeById(long synergyId) {
repository.deleteById(synergyId);
}
@Override
public void saveSynergy(Synergy synergy) {
repository.save(synergy);
}
@Override
public List<Synergy> getAllSynergies() {
return repository.findAll();
}
}

View File

@ -0,0 +1,12 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.TextDataVal;
import java.util.List;
public interface TextDataValService {
List<TextDataVal> getAllTexts();
TextDataVal getTextDataValById(long textId);
TextDataVal save(TextDataVal val);
}

View File

@ -0,0 +1,32 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.TextDataVal;
import com.alterdekim.javabot.repository.TextDataValRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class TextDataValServiceImpl implements TextDataValService {
private final TextDataValRepository repository;
public TextDataValServiceImpl(TextDataValRepository repository) {
this.repository = repository;
}
@Override
public List<TextDataVal> getAllTexts() {
return repository.findAll();
}
@Override
public TextDataVal getTextDataValById(long textId) {
return repository.findById(textId).orElse(null);
}
@Override
public TextDataVal save(TextDataVal val) {
return repository.save(val);
}
}

View File

@ -0,0 +1,14 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.dto.UserDTO;
import com.alterdekim.javabot.entities.User;
import java.util.List;
public interface UserService {
void saveUser(UserDTO userDto);
User findByUsername(String usernane);
List<UserDTO> findAllUsers();
}

View File

@ -0,0 +1,67 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.dto.UserDTO;
import com.alterdekim.javabot.entities.Role;
import com.alterdekim.javabot.entities.User;
import com.alterdekim.javabot.repository.RoleRepository;
import com.alterdekim.javabot.repository.UserRepository;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final RoleRepository roleRepository;
private final PasswordEncoder passwordEncoder;
public UserServiceImpl(UserRepository userRepository,
RoleRepository roleRepository,
PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
this.passwordEncoder = passwordEncoder;
}
@Override
public void saveUser(UserDTO userDto) {
User user = new User();
user.setUsername(userDto.getUsername());
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
Role role = roleRepository.findByName("ROLE_ADMIN");
if(role == null){
role = checkRoleExist();
}
user.setRoles(Collections.singletonList(role));
userRepository.save(user);
}
@Override
public User findByUsername(String username) {
return userRepository.findByUsername(username);
}
@Override
public List<UserDTO> findAllUsers() {
List<User> users = userRepository.findAll();
return users.stream().map(this::convertEntityToDto)
.collect(Collectors.toList());
}
private UserDTO convertEntityToDto(User user){
UserDTO userDto = new UserDTO();
userDto.setUsername(user.getUsername());
return userDto;
}
private Role checkRoleExist() {
Role role = new Role();
role.setName("ROLE_ADMIN");
return roleRepository.save(role);
}
}

View File

@ -0,0 +1,17 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Synergy;
import com.alterdekim.javabot.entities.Work;
import java.util.List;
public interface WorkService {
List<Work> getAllWorks();
Work getWorkById(long workId);
void removeById(long workId);
void saveWork(Work work);
List<Synergy> getSynergies(Long id);
}

View File

@ -0,0 +1,43 @@
package com.alterdekim.javabot.service;
import com.alterdekim.javabot.entities.Synergy;
import com.alterdekim.javabot.entities.Work;
import com.alterdekim.javabot.repository.WorkRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class WorkServiceImpl implements WorkService {
private final WorkRepository repository;
public WorkServiceImpl(WorkRepository repository) {
this.repository = repository;
}
@Override
public List<Work> getAllWorks() {
return repository.findAll();
}
@Override
public Work getWorkById(long workId) {
return repository.findById(workId).orElse(null);
}
@Override
public void removeById(long workId) {
repository.deleteById(workId);
}
@Override
public void saveWork(Work work) {
repository.save(work);
}
@Override
public List<Synergy> getSynergies(Long id) {
return repository.getSynergies(id);
}
}

View File

@ -0,0 +1,106 @@
package com.alterdekim.javabot.storage;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.stream.Stream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.multipart.MultipartFile;
@Service
public class FileSystemStorageService implements StorageService {
private final Path rootLocation;
@Autowired
public FileSystemStorageService(StorageProperties properties) {
if(properties.getLocation().trim().isEmpty()){
throw new StorageException("File upload location can not be Empty.");
}
this.rootLocation = Paths.get(new ClassPathResource(properties.getLocation()).getPath());
}
@Override
public void store(MultipartFile file) {
try {
if (file.isEmpty()) {
throw new StorageException("Failed to store empty file.");
}
Path destinationFile = this.rootLocation.resolve(
Paths.get(file.getOriginalFilename()))
.normalize().toAbsolutePath();
if (!destinationFile.getParent().equals(this.rootLocation.toAbsolutePath())) {
throw new StorageException(
"Cannot store file outside current directory.");
}
try (InputStream inputStream = file.getInputStream()) {
Files.copy(inputStream, destinationFile,
StandardCopyOption.REPLACE_EXISTING);
}
}
catch (IOException e) {
throw new StorageException("Failed to store file.", e);
}
}
@Override
public Stream<Path> loadAll() {
try {
return Files.walk(this.rootLocation, 1)
.filter(path -> !path.equals(this.rootLocation))
.map(this.rootLocation::relativize);
}
catch (IOException e) {
throw new StorageException("Failed to read stored files", e);
}
}
@Override
public Path load(String filename) {
return rootLocation.resolve(filename);
}
@Override
public Resource loadAsResource(String filename) {
try {
Resource resource = new ClassPathResource(filename);
if (resource.exists() || resource.isReadable()) {
return resource;
}
else {
throw new StorageFileNotFoundException(
"Could not read file: " + filename);
}
}
catch (Exception e) {
throw new StorageFileNotFoundException("Could not read file: " + filename, e);
}
}
@Override
public void deleteAll() {
FileSystemUtils.deleteRecursively(rootLocation.toFile());
}
@Override
public void init() {
// try {
// Files.createDirectories(rootLocation);
// }
// catch (IOException e) {
// throw new StorageException("Could not initialize storage", e);
// }
}
}

View File

@ -0,0 +1,13 @@
package com.alterdekim.javabot.storage;
public class StorageException extends RuntimeException {
public StorageException(String message) {
super(message);
}
public StorageException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,12 @@
package com.alterdekim.javabot.storage;
public class StorageFileNotFoundException extends StorageException {
public StorageFileNotFoundException(String message) {
super(message);
}
public StorageFileNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,13 @@
package com.alterdekim.javabot.storage;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Setter
@Getter
@ConfigurationProperties("storage")
public class StorageProperties {
private String location = "static";
}

View File

@ -0,0 +1,23 @@
package com.alterdekim.javabot.storage;
import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;
import java.nio.file.Path;
import java.util.stream.Stream;
public interface StorageService {
void init();
void store(MultipartFile file);
Stream<Path> loadAll();
Path load(String filename);
Resource loadAsResource(String filename);
void deleteAll();
}

View File

@ -0,0 +1,99 @@
package com.alterdekim.javabot.util;
import com.alterdekim.javabot.Constants;
import com.alterdekim.javabot.bot.InfoSections;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup;
import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton;
import java.util.*;
public class BotUtils {
public static InlineKeyboardMarkup getJoinKeyboard() {
InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
List<List<InlineKeyboardButton>> columns = new ArrayList<>();
InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
inlineKeyboardButton.setText(Constants.JOIN_GAME_BTN);
inlineKeyboardButton.setCallbackData(HashUtils.bytesToHex(Constants.JOIN_GAME_BTN.getBytes()));
List<InlineKeyboardButton> keyboardButtonsRow1 = new ArrayList<>();
keyboardButtonsRow1.add(inlineKeyboardButton);
columns.add(keyboardButtonsRow1);
InlineKeyboardButton inlineKeyboardButton1 = new InlineKeyboardButton();
inlineKeyboardButton1.setText(Constants.START_GAME_BTN);
inlineKeyboardButton1.setCallbackData(HashUtils.bytesToHex(Constants.START_GAME_BTN.getBytes()));
List<InlineKeyboardButton> keyboardButtonsRow2 = new ArrayList<>();
keyboardButtonsRow2.add(inlineKeyboardButton1);
columns.add(keyboardButtonsRow2);
inlineKeyboardMarkup.setKeyboard(columns);
return inlineKeyboardMarkup;
}
public static InlineKeyboardMarkup getShowKeyboard(InfoSections infoSections) {
InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
List<List<InlineKeyboardButton>> columns = new ArrayList<>();
if( !infoSections.getIsHobbyShowed() ) {
InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
inlineKeyboardButton.setText(Constants.HOBBY_BTN);
inlineKeyboardButton.setCallbackData(HashUtils.bytesToHex(Constants.HOBBY_BTN.getBytes()));
columns.add(Collections.singletonList(inlineKeyboardButton));
}
if( !infoSections.getIsAgeShowed() ) {
InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
inlineKeyboardButton.setText(Constants.AGE_BTN);
inlineKeyboardButton.setCallbackData(HashUtils.bytesToHex(Constants.AGE_BTN.getBytes()));
columns.add(Collections.singletonList(inlineKeyboardButton));
}
if( !infoSections.getIsGenderShowed() ) {
InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
inlineKeyboardButton.setText(Constants.GENDER_BTN);
inlineKeyboardButton.setCallbackData(HashUtils.bytesToHex(Constants.GENDER_BTN.getBytes()));
columns.add(Collections.singletonList(inlineKeyboardButton));
}
if( !infoSections.getIsHealthShowed() ) {
InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
inlineKeyboardButton.setText(Constants.HEALTH_BTN);
inlineKeyboardButton.setCallbackData(HashUtils.bytesToHex(Constants.HEALTH_BTN.getBytes()));
columns.add(Collections.singletonList(inlineKeyboardButton));
}
if( !infoSections.getIsWorkShowed() ) {
InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
inlineKeyboardButton.setText(Constants.WORK_BTN);
inlineKeyboardButton.setCallbackData(HashUtils.bytesToHex(Constants.WORK_BTN.getBytes()));
columns.add(Collections.singletonList(inlineKeyboardButton));
}
if( !infoSections.getIsLuggageShowed() ) {
InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
inlineKeyboardButton.setText(Constants.LUGG_BTN);
inlineKeyboardButton.setCallbackData(HashUtils.bytesToHex(Constants.LUGG_BTN.getBytes()));
columns.add(Collections.singletonList(inlineKeyboardButton));
}
inlineKeyboardMarkup.setKeyboard(columns);
return inlineKeyboardMarkup;
}
public static InlineKeyboardMarkup getGameTypeKeyboard() {
InlineKeyboardMarkup inlineKeyboardMarkup = new InlineKeyboardMarkup();
List<List<InlineKeyboardButton>> columns = new ArrayList<>();
InlineKeyboardButton inlineKeyboardButton = new InlineKeyboardButton();
inlineKeyboardButton.setText(Constants.PROBABILITY);
inlineKeyboardButton.setCallbackData(HashUtils.bytesToHex(Constants.PROBABILITY.getBytes()));
List<InlineKeyboardButton> keyboardButtonsRow1 = new ArrayList<>();
keyboardButtonsRow1.add(inlineKeyboardButton);
columns.add(keyboardButtonsRow1);
InlineKeyboardButton inlineKeyboardButton1 = new InlineKeyboardButton();
inlineKeyboardButton1.setText(Constants.DEATHMATCH);
inlineKeyboardButton1.setCallbackData(HashUtils.bytesToHex(Constants.DEATHMATCH.getBytes()));
List<InlineKeyboardButton> keyboardButtonsRow2 = new ArrayList<>();
keyboardButtonsRow2.add(inlineKeyboardButton1);
columns.add(keyboardButtonsRow2);
inlineKeyboardMarkup.setKeyboard(columns);
return inlineKeyboardMarkup;
}
public static String mentionUser(String name) {
return " @"+name+" ";
}
public static Object getRandomFromList(List list, Random random) {
return list.get(random.nextInt(list.size()));
}
}

View File

@ -0,0 +1,7 @@
package com.alterdekim.javabot.util;
public class Clamp {
public static double clamp(double value, double min, double max) {
return Math.max(min, Math.min(max, value));
}
}

View File

@ -0,0 +1,7 @@
package com.alterdekim.javabot.util;
public enum GameState {
NONE,
JOINING,
STARTED
}

View File

@ -0,0 +1,59 @@
package com.alterdekim.javabot.util;
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
@Slf4j
public class HashUtils {
public static String md5(String s) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] theMD5digest = md.digest(s.getBytes(StandardCharsets.UTF_8));
return bytesToHex(theMD5digest);
} catch ( Exception e ) {
log.error(e.getMessage());
}
return null;
}
public static byte[] decodeHexString(String hexString) {
if (hexString.length() % 2 == 1) {
throw new IllegalArgumentException(
"Invalid hexadecimal String supplied.");
}
byte[] bytes = new byte[hexString.length() / 2];
for (int i = 0; i < hexString.length(); i += 2) {
bytes[i / 2] = hexToByte(hexString.substring(i, i + 2));
}
return bytes;
}
private static byte hexToByte(String hexString) {
int firstDigit = toDigit(hexString.charAt(0));
int secondDigit = toDigit(hexString.charAt(1));
return (byte) ((firstDigit << 4) + secondDigit);
}
private static int toDigit(char hexChar) {
int digit = Character.digit(hexChar, 16);
if(digit == -1) {
throw new IllegalArgumentException(
"Invalid Hexadecimal Character: "+ hexChar);
}
return digit;
}
private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
public static String bytesToHex(byte[] bytes) {
byte[] hexChars = new byte[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars, StandardCharsets.UTF_8);
}
}

View File

@ -0,0 +1,27 @@
package com.alterdekim.javabot.util;
public class PseudoRandom {
private final double seed;
private double i = 0;
public PseudoRandom( double seed ) {
assert seed > 0 && seed < 1;
this.seed = seed;
}
public PseudoRandom() {
this.seed = 1;
}
public double next() {
double f = ((Math.cos(3d * i) * Math.cos(3d * i) * Math.sin(3d * i)) * 1.3333d * Math.cos(i) * Math.cos(i)) + 0.5d;
i += seed;
i = i % 6.2d;
return f;
}
public int nextInt(int bound) {
return (int) Math.floor(next() * ((double) bound));
}
}

View File

@ -0,0 +1,490 @@
package com.alterdekim.javabot.util;
/* *******************************************
// LICENSE INFORMATION
// The code, "Detecting Smartphones Using PHP"
// by Anthony Hand, is licensed under a Creative Commons
// Attribution 3.0 United States License.
//
// Updated 01 March 2010 by Bryan J Swift
// - Remove un-needed if statements instead just returning the boolean
// inside the if clause
//
// Updated 14 December 2009 by A Hand
// - Added the method for detecting BlackBerry Touch
// devices like the Storm 1 and 2.
//
// Updated 5 December 2009 by A Hand
// - Fixed the DetectPalmOS method. It should filter
// out WebOS devices.
//
// Updated 8 November 2009 by A Hand
// - Added the deviceWebOS variable.
// - Added Palm's WebOS to the DetectPalmOS method.
// - Created a new method to check for Palm's WebOS devices.
// - Added Palm's WebOS to the DetectTierIphone method.
//
// Updated 4 April 2009 by A Hand
// - Changed the name of the class from DetectSmartPhone to UAgentInfo.
// New name is more consistent with PHP and JavaScript classes.
// - Added a method to detect Opera Mobile and Mini. Opera Mobile is new.
// - Updated the algorithm for detecting a Sony Mylo.
// - Added Android to the DetectTierIphone method.
// - Updated comments for Detect Tier methods.
//
// Updated 22 January 2009
// - Ported to Java from the PHP code by
// Satish Kumar Nookala, javasatish@yahoo.com
//
// Anthony Hand, ahand@hand-interactive.com
// Web: www.hand-interactive.com
//
// License info: http://creativecommons.org/licenses/by/3.0/us/
//
// This code is provided AS IS with no expressed or implied warranty.
// You have the right to use this code or any portion of it
// so long as you provide credit toward myself as the original author.
//
// *******************************************
*/
/**
* The DetectSmartPhone class encapsulates information about
* a browser's connection to your web site.
* You can use it to find out whether the browser asking for
* your site's content is probably running on a mobile device.
* The methods were written so you can be as granular as you want.
* For example, enquiring whether it's as specific as an iPod Touch or
* as general as a smartphone class device.
* The object's methods return true, or false.
*/
public class UAgentInfo {
// User-Agent and Accept HTTP request headers
private String userAgent = "";
private String httpAccept = "";
// Initialize some initial smartphone string variables.
public static final String engineWebKit = "webkit";
public static final String deviceAndroid = "android";
public static final String deviceIphone = "iphone";
public static final String deviceIpod = "ipod";
public static final String deviceSymbian = "symbian";
public static final String deviceS60 = "series60";
public static final String deviceS70 = "series70";
public static final String deviceS80 = "series80";
public static final String deviceS90 = "series90";
public static final String deviceWinMob = "windows ce";
public static final String deviceWindows = "windows";
public static final String deviceIeMob = "iemobile";
public static final String enginePie = "wm5 pie"; //An old Windows Mobile
public static final String deviceBB = "blackberry";
public static final String vndRIM = "vnd.rim"; //Detectable when BB devices emulate IE or Firefox
public static final String deviceBBStorm = "blackberry95"; //Storm 1 and 2
public static final String devicePalm = "palm";
public static final String deviceWebOS = "webos"; //For Palm's new WebOS devices
public static final String engineBlazer = "blazer"; //Old Palm
public static final String engineXiino = "xiino"; //Another old Palm
//Initialize variables for mobile-specific content.
public static final String vndwap = "vnd.wap";
public static final String wml = "wml";
//Initialize variables for other random devices and mobile browsers.
public static final String deviceBrew = "brew";
public static final String deviceDanger = "danger";
public static final String deviceHiptop = "hiptop";
public static final String devicePlaystation = "playstation";
public static final String deviceNintendoDs = "nitro";
public static final String deviceNintendo = "nintendo";
public static final String deviceWii = "wii";
public static final String deviceXbox = "xbox";
public static final String deviceArchos = "archos";
public static final String engineOpera = "opera"; //Popular browser
public static final String engineNetfront = "netfront"; //Common embedded OS browser
public static final String engineUpBrowser = "up.browser"; //common on some phones
public static final String engineOpenWeb = "openweb"; //Transcoding by OpenWave server
public static final String deviceMidp = "midp"; //a mobile Java technology
public static final String uplink = "up.link";
public static final String devicePda = "pda"; //some devices report themselves as PDAs
public static final String mini = "mini"; //Some mobile browsers put "mini" in their names.
public static final String mobile = "mobile"; //Some mobile browsers put "mobile" in their user agent strings.
public static final String mobi = "mobi"; //Some mobile browsers put "mobi" in their user agent strings.
//Use Maemo, Tablet, and Linux to test for Nokia"s Internet Tablets.
public static final String maemo = "maemo";
public static final String maemoTablet = "tablet";
public static final String linux = "linux";
public static final String qtembedded = "qt embedded"; //for Sony Mylo
public static final String mylocom2 = "com2"; //for Sony Mylo also
//In some UserAgents, the only clue is the manufacturer.
public static final String manuSonyEricsson = "sonyericsson";
public static final String manuericsson = "ericsson";
public static final String manuSamsung1 = "sec-sgh";
public static final String manuSony = "sony";
//In some UserAgents, the only clue is the operator.
public static final String svcDocomo = "docomo";
public static final String svcKddi = "kddi";
public static final String svcVodafone = "vodafone";
// Standard desktop browser detection strings
public static final String msie = "MSIE";
public static final String msie60 = "MSIE 6.0";
public static final String msie61 = "MSIE 6.1";
public static final String msie70 = "MSIE 7.0";
public static final String msie8 = "MSIE 8.0";
public static final String msie9 = "MSIE 9.0";
/**
* Initialize the userAgent and httpAccept variables
*
* @param userAgent the User-Agent header
* @param httpAccept the Accept header
*/
public UAgentInfo(String userAgent, String httpAccept) {
if (userAgent != null) {
this.userAgent = userAgent.toLowerCase();
}
if (httpAccept != null) {
this.httpAccept = httpAccept.toLowerCase();
}
}
/**
* Return the lower case HTTP_USER_AGENT
*/
public String getUserAgent() {
return userAgent;
}
/**
* Return the lower case HTTP_ACCEPT
*/
public String getHttpAccept() {
return httpAccept;
}
/**
* Detects if the current device is an iPhone.
*/
public boolean detectIphone() {
// The iPod touch says it's an iPhone! So let's disambiguate.
return userAgent.indexOf(deviceIphone) != -1 && !detectIpod();
}
/**
* Detects if the current device is an iPod Touch.
*/
public boolean detectIpod() {
return userAgent.indexOf(deviceIpod) != -1;
}
/**
* Detects if the current device is an iPhone or iPod Touch.
*/
public boolean detectIphoneOrIpod() {
//We repeat the searches here because some iPods may report themselves as an iPhone, which would be okay.
return userAgent.indexOf(deviceIphone) != -1 || userAgent.indexOf(deviceIpod) != -1;
}
/**
* Detects if the current device is an Android OS-based device.
*/
public boolean detectAndroid() {
return userAgent.indexOf(deviceAndroid) != -1;
}
/**
* Detects if the current device is an Android OS-based device and
* the browser is based on WebKit.
*/
public boolean detectAndroidWebKit() {
return detectAndroid() && detectWebkit();
}
/**
* Detects if the current browser is based on WebKit.
*/
public boolean detectWebkit() {
return userAgent.indexOf(engineWebKit) != -1;
}
/**
* Detects if the current browser is the S60 Open Source Browser.
*/
public boolean detectS60OssBrowser() {
//First, test for WebKit, then make sure it's either Symbian or S60.
return detectWebkit() && (userAgent.indexOf(deviceSymbian) != -1 || userAgent.indexOf(deviceS60) != -1);
}
/**
*
* Detects if the current device is any Symbian OS-based device,
* including older S60, Series 70, Series 80, Series 90, and UIQ,
* or other browsers running on these devices.
*/
public boolean detectSymbianOS() {
return userAgent.indexOf(deviceSymbian) != -1 || userAgent.indexOf(deviceS60) != -1 ||
userAgent.indexOf(deviceS70) != -1 || userAgent.indexOf(deviceS80) != -1 ||
userAgent.indexOf(deviceS90) != -1;
}
/**
* Detects if the current browser is a Windows Mobile device.
*/
public boolean detectWindowsMobile() {
//Most devices use 'Windows CE', but some report 'iemobile'
// and some older ones report as 'PIE' for Pocket IE.
return userAgent.indexOf(deviceWinMob) != -1 ||
userAgent.indexOf(deviceIeMob) != -1 ||
userAgent.indexOf(enginePie) != -1 ||
(detectWapWml() && userAgent.indexOf(deviceWindows) != -1);
}
/**
* Detects if the current browser is a BlackBerry of some sort.
*/
public boolean detectBlackBerry() {
return userAgent.indexOf(deviceBB) != -1 || httpAccept.indexOf(vndRIM) != -1;
}
/**
* Detects if the current browser is a BlackBerry Touch
* device, such as the Storm
*/
public boolean detectBlackBerryTouch() {
return userAgent.indexOf(deviceBBStorm) != -1;
}
/**
* Detects if the current browser is on a PalmOS device.
*/
public boolean detectPalmOS() {
//Most devices nowadays report as 'Palm', but some older ones reported as Blazer or Xiino.
if (userAgent.indexOf(devicePalm) != -1 || userAgent.indexOf(engineBlazer) != -1 ||
userAgent.indexOf(engineXiino) != -1 && !detectPalmWebOS()) {
//Make sure it's not WebOS first
if (detectPalmWebOS()) { return false; }
else { return true; }
}
return false;
}
/**
* Detects if the current browser is on a Palm device
* running the new WebOS.
*/
public boolean detectPalmWebOS() {
return userAgent.indexOf(deviceWebOS) != -1;
}
/**
* Check to see whether the device is any device
* in the 'smartphone' category.
*/
public boolean detectSmartphone() {
return (detectIphoneOrIpod() ||
detectS60OssBrowser() ||
detectSymbianOS() ||
detectWindowsMobile() ||
detectBlackBerry() ||
detectPalmOS() ||
detectPalmWebOS() ||
detectAndroid());
}
/**
* Detects whether the device is a Brew-powered device.
*/
public boolean detectBrewDevice() {
return userAgent.indexOf(deviceBrew) != -1;
}
/**
* Detects the Danger Hiptop device.
*/
public boolean detectDangerHiptop() {
return userAgent.indexOf(deviceDanger) != -1 || userAgent.indexOf(deviceHiptop) != -1;
}
/**
* Detects Opera Mobile or Opera Mini.
* Added by AHand
*/
public boolean detectOperaMobile() {
return userAgent.indexOf(engineOpera) != -1 && (userAgent.indexOf(mini) != -1 || userAgent.indexOf(mobi) != -1);
}
/**
* Detects whether the device supports WAP or WML.
*/
public boolean detectWapWml() {
return httpAccept.indexOf(vndwap) != -1 || httpAccept.indexOf(wml) != -1;
}
/**
* The quick way to detect for a mobile device.
* Will probably detect most recent/current mid-tier Feature Phones
* as well as smartphone-class devices.
*/
public boolean detectMobileQuick() {
//Ordered roughly by market share, WAP/XML > Brew > Smartphone.
if (detectWapWml()) { return true; }
if (detectBrewDevice()) { return true; }
// Updated by AHand
if (detectOperaMobile()) { return true; }
if (userAgent.indexOf(engineUpBrowser) != -1) { return true; }
if (userAgent.indexOf(engineOpenWeb) != -1) { return true; }
if (userAgent.indexOf(deviceMidp) != -1) { return true; }
if (detectSmartphone()) { return true; }
if (detectDangerHiptop()) { return true; }
if (detectMidpCapable()) { return true; }
if (userAgent.indexOf(devicePda) != -1) { return true; }
if (userAgent.indexOf(mobile) != -1) { return true; }
//detect older phones from certain manufacturers and operators.
if (userAgent.indexOf(uplink) != -1) { return true; }
if (userAgent.indexOf(manuSonyEricsson) != -1) { return true; }
if (userAgent.indexOf(manuericsson) != -1) { return true; }
if (userAgent.indexOf(manuSamsung1) != -1) { return true; }
if (userAgent.indexOf(svcDocomo) != -1) { return true; }
if (userAgent.indexOf(svcKddi) != -1) { return true; }
if (userAgent.indexOf(svcVodafone) != -1) { return true; }
return false;
}
/**
* Detects if the current device is a Sony Playstation.
*/
public boolean detectSonyPlaystation() {
return userAgent.indexOf(devicePlaystation) != -1;
}
/**
* Detects if the current device is a Nintendo game device.
*/
public boolean detectNintendo() {
return userAgent.indexOf(deviceNintendo) != -1 || userAgent.indexOf(deviceWii) != -1 ||
userAgent.indexOf(deviceNintendoDs) != -1;
}
/**
* Detects if the current device is a Microsoft Xbox.
*/
public boolean detectXbox() {
return userAgent.indexOf(deviceXbox) != -1;
}
/**
* Detects if the current device is an Internet-capable game console.
*/
public boolean detectGameConsole() {
return detectSonyPlaystation() || detectNintendo() || detectXbox();
}
/**
* Detects if the current device supports MIDP, a mobile Java technology.
*/
public boolean detectMidpCapable() {
return userAgent.indexOf(deviceMidp) != -1 || httpAccept.indexOf(deviceMidp) != -1;
}
/**
* Detects if the current device is on one of the Maemo-based Nokia Internet Tablets.
*/
public boolean detectMaemoTablet() {
return (userAgent.indexOf(maemo) != -1 || (userAgent.indexOf(maemoTablet) != -1 && userAgent.indexOf(linux) != -1));
}
/**
* Detects if the current device is an Archos media player/Internet tablet.
*/
public boolean detectArchos() {
return userAgent.indexOf(deviceArchos) != -1;
}
/**
* Detects if the current browser is a Sony Mylo device.
* Updated by AHand
*/
public boolean detectSonyMylo() {
return userAgent.indexOf(manuSony) != -1 && (userAgent.indexOf(qtembedded) != -1 ||
userAgent.indexOf(mylocom2) != -1);
}
/**
* The longer and more thorough way to detect for a mobile device.
* Will probably detect most feature phones,
* smartphone-class devices, Internet Tablets,
* Internet-enabled game consoles, etc.
* This ought to catch a lot of the more obscure and older devices, also --
* but no promises on thoroughness!
*/
public boolean detectMobileLong() {
return detectMobileQuick() || detectMaemoTablet() || detectGameConsole();
}
//*****************************
// For Desktop Browsers
//*****************************
public boolean detectMSIE() {
return userAgent.indexOf(msie) != -1;
}
public boolean detectMSIE6() {
return userAgent.indexOf(msie60) != -1 && userAgent.indexOf(msie61) != -1;
}
public boolean detectMSIE7() {
return userAgent.indexOf(msie70) != -1;
}
public boolean detectMSIE8() {
return userAgent.indexOf(msie8) != -1;
}
public boolean detectMSIE9() {
return userAgent.indexOf(msie9) != -1;
}
//*****************************
// For Mobile Web Site Design
//*****************************
/**
* The quick way to detect for a tier of devices.
* This method detects for devices which can
* display iPhone-optimized web content.
* Includes iPhone, iPod Touch, Android, Palm WebOS, etc.
*/
public boolean detectTierIphone() {
return detectIphoneOrIpod() || detectPalmWebOS() || detectAndroid() || detectAndroidWebKit();
}
/**
* The quick way to detect for a tier of devices.
* This method detects for all smartphones, but
* excludes the iPhone Tier devices.
*/
public boolean detectTierSmartphones() {
return detectSmartphone() && (!detectTierIphone());
}
/**
* The quick way to detect for a tier of devices.
* This method detects for all other types of phones,
* but excludes the iPhone and Smartphone Tier devices.
*/
public boolean detectTierOtherPhones() {
return detectMobileQuick() && (!detectTierIphone()) && (!detectTierSmartphones());
}
}

View File

@ -0,0 +1,57 @@
signup=Sign up
signin=Sign in
remember=Remember me
pass=Password
nname=Nickname
login_error=Incorrect nickname or password
invitecode=Invite code
incorcred=Incorrect credentials
logote=Logout
rlang=Russian
elang=English
langl=Language
abby=Abilities
diss=Disasters
lugg=Luggage
heeal=Health
pprof=Profession
hhobb=Hobby
aggend=Gender
addata=Additional Data
pplay=Player
hhome=Home
aaccden=Access denied
oopsi=Opps!
badway= You're in a bad way.
light=Light
dark=Dark
auto=Auto
toggleth=Toggle theme
aedentry=Add(or edit) entry
inlcandie=Can die?
inlcfr=Childfree?
inlismal=Male?
inlisfem=Female?
inlgente=Gender label:
inlfood=Food
inlheal=Asocial
inlpower=Power
inlviol=Violence
hobblab=Hobby label:
inlgarb=Garbage?
inlaname=Name label:
inladesc=Description label:
inlhin=Health index
cancbt=Cancel
adddbtn=Add
edditbtn=Edit
remombtn=Remove
inlprobbb=Add probability
eddittsynn=Edit synergies
firentname=First entity name
firenttype=First entity type
secentname=Second entity name
secenttype=Second entity type
probbval=Probability value
actionbtn=Action
stats=Statistics

View File

@ -0,0 +1,57 @@
signup=Регистрация
signin=Войти
remember=Запомнить
pass=Пароль
nname=Ник
login_error=Неправильный ник или пароль
invitecode=Инвайт
incorcred=Неправильные данные
logote=Выйти
rlang=Русский
elang=Английский
langl=Язык
abby=Способности
diss=Катастрофы
lugg=Багаж
heeal=Здоровье
pprof=Профессия
hhobb=Хобби
aggend=Гендер
addata=Факты
pplay=Игрок
hhome=Главная
aaccden=Доступ запрещен
oopsi=Упс!
badway= Вас сюда не звали.
light=Светлая
dark=Тёмная
auto=Авто
toggleth=Переключить
aedentry=Добавить/изменить запись
inlcandie=Может умереть?
inlcfr=Бесплоден?
inlismal=Мужчина?
inlisfem=Женщина?
inlgente=Гендер(текст):
inlfood=Еда
inlheal=Асоциальность
inlpower=Сила
inlviol=Насилие
hobblab=Название хобби:
inlgarb=Мусор?
inlaname=Имя:
inladesc=Описание:
inlhin=Здоровье
cancbt=Отмена
adddbtn=Добавить
edditbtn=Изменить
remombtn=Удалить
inlprobbb=Add probability
eddittsynn=Синергии
firentname=Имя первой вещи
firenttype=Тип первой вещи
secentname=Имя второй вещи
secenttype=Тип второй вещи
probbval=Вероятность(знач)
actionbtn=Действие
stats=Статистика

View File

@ -0,0 +1,4 @@
html,
body {
height: 100%;
}

View File

@ -0,0 +1,25 @@
html,
body {
height: 100%;
}
.form-signin {
max-width: 330px;
padding: 1rem;
}
.form-signin .form-floating:focus-within {
z-index: 2;
}
.form-signin input[type="text"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}

View File

@ -0,0 +1,13 @@
html,
body {
height: 100%;
}
.form-signup {
max-width: 330px;
padding: 1rem;
}
.form-signup .form-floating:focus-within {
z-index: 2;
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,80 @@
/*!
* Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)
* Copyright 2011-2023 The Bootstrap Authors
* Licensed under the Creative Commons Attribution 3.0 Unported License.
*/
(() => {
'use strict'
const getStoredTheme = () => localStorage.getItem('theme')
const setStoredTheme = theme => localStorage.setItem('theme', theme)
const getPreferredTheme = () => {
const storedTheme = getStoredTheme()
if (storedTheme) {
return storedTheme
}
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
const setTheme = theme => {
if (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-bs-theme', 'dark')
} else {
document.documentElement.setAttribute('data-bs-theme', theme)
}
}
setTheme(getPreferredTheme())
const showActiveTheme = (theme, focus = false) => {
const themeSwitcher = document.querySelector('#bd-theme')
if (!themeSwitcher) {
return
}
const themeSwitcherText = document.querySelector('#bd-theme-text')
const activeThemeIcon = document.querySelector('.theme-icon-active use')
const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`)
const svgOfActiveBtn = btnToActive.querySelector('svg use').getAttribute('href')
document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
element.classList.remove('active')
element.setAttribute('aria-pressed', 'false')
})
btnToActive.classList.add('active')
btnToActive.setAttribute('aria-pressed', 'true')
activeThemeIcon.setAttribute('href', svgOfActiveBtn)
const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`
themeSwitcher.setAttribute('aria-label', themeSwitcherLabel)
if (focus) {
themeSwitcher.focus()
}
}
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
const storedTheme = getStoredTheme()
if (storedTheme !== 'light' && storedTheme !== 'dark') {
setTheme(getPreferredTheme())
}
})
window.addEventListener('DOMContentLoaded', () => {
showActiveTheme(getPreferredTheme())
document.querySelectorAll('[data-bs-theme-value]')
.forEach(toggle => {
toggle.addEventListener('click', () => {
const theme = toggle.getAttribute('data-bs-theme-value')
setStoredTheme(theme)
setTheme(theme)
showActiveTheme(theme, true)
})
})
})
})()

View File

@ -0,0 +1,256 @@
function str_toHex(s) {
// utf8 to latin1
var s = unescape(encodeURIComponent(s));
var h = '';
for (var i = 0; i < s.length; i++) {
h += s.charCodeAt(i).toString(16);
}
return h;
}
function grab_form() {
let arr = [];
$("form#entryForm :input").each(function() {
var input = $(this);
let query = "";
if(input.attr("type") == "checkbox") {
query = input.attr('id') + "=" + input.is(':checked');
} else if(input.attr("type") == "text") {
var vv = str_toHex(input.val());
query = input.attr('id') + "=" + vv;
} else {
query = input.attr('id') + "=" + input.val();
}
arr.push(query);
});
arr.push("section=" + new URL(window.location.href).searchParams.get("section"));
return arr.join("&");
}
function add_entry_modal() {
$("#modal_submit_edit").css("display", "none");
$("#modal_submit_add").css("display", "");
}
function form_agge(jobj) {
get_text_api(jobj.genderTextId, function(t) {
$("#gender_text").val(t);
});
$("#ismale").prop('checked', jobj.isMale);
$("#isfemale").prop('checked', jobj.isFemale);
$("#childFree").prop('checked', jobj.isChildfree);
$("#canDie").prop('checked', jobj.canDie);
}
function form_hobb(jobj) {
get_text_api(jobj.textDescId, function(t) {
$("#hobby_text").val(t);
});
$("#violenceRange").val(jobj.violence);
$("#powerRange").val(jobj.power);
$("#healRange").val(jobj.heal);
$("#foodRange").val(jobj.food);
}
function form_lugg(jobj) {
get_text_api(jobj.textNameId, function(t) {
$("#luggage_name_text").val(t);
});
get_text_api(jobj.textDescId, function(t) {
$("#luggage_desc_text").val(t);
});
$("#violenceRange").val(jobj.violence);
$("#powerRange").val(jobj.power);
$("#healRange").val(jobj.heal);
$("#foodRange").val(jobj.food);
$("#isgarbage").prop('checked', jobj.garbage);
}
function form_prof(jobj) {
get_text_api(jobj.textNameId, function(t) {
$("#work_name_text").val(t);
});
get_text_api(jobj.textDescId, function(t) {
$("#work_desc_text").val(t);
});
$("#violenceRange").val(jobj.violence);
$("#powerRange").val(jobj.power);
$("#healRange").val(jobj.heal);
$("#foodRange").val(jobj.food);
}
function form_heal(jobj) {
get_text_api(jobj.textNameId, function(t) {
$("#heal_name_text").val(t);
});
get_text_api(jobj.textDescId, function(t) {
$("#heal_desc_text").val(t);
});
$("#health_index").val(jobj.health_index);
}
function form_disaster(jobj) {
get_text_api(jobj.nameTextId, function(t) {
$("#diss_name_text").val(t);
});
get_text_api(jobj.descTextId, function(t) {
$("#diss_desc_text").val(t);
});
}
function show_modal_edit(jobj, oid) {
var section = new URL(window.location.href).searchParams.get("section");
switch(section) {
case "agge":
form_agge(jobj);
break;
case "hobb":
form_hobb(jobj);
break;
case "lugg":
form_lugg(jobj);
break;
case "heal":
form_heal(jobj);
break;
case "prof":
form_prof(jobj);
break;
case "diss":
form_disaster(jobj);
break;
default:
form_disaster(jobj);
break;
}
$("#modal_submit_edit").css("display", "");
$("#modal_submit_add").css("display", "none");
$("#modal_submit_edit").attr("data-entry-id", oid);
}
function edit_submit_entry(obj) {
$.post("/api/remove_entry", "section="+new URL(window.location.href).searchParams.get("section")+"&entry_id="+($(obj).attr("data-entry-id")), function(data, status) {
$.post("/api/add_entry", grab_form(), function(data, status) {
window.location.reload();
});
});
}
function edit_entry(obj) {
$.post("/api/edit_entry", "section="+new URL(window.location.href).searchParams.get("section")+"&entry_id="+($(obj).attr("data-id")), function(data, status) {
var jobj = JSON.parse(data);
show_modal_edit(jobj, $(obj).attr("data-id"));
});
}
function get_text_api(tid, cb) {
$.post("/api/getTextById", "entry_id="+tid, function(data, status) {
cb(data);
});
}
function remove_entry(obj) {
$.post("/api/remove_entry", "section="+new URL(window.location.href).searchParams.get("section")+"&entry_id="+($(obj).attr("data-id")), function(data, status) {
window.location.reload();
});
}
function add_entry() {
$.post("/api/add_entry", grab_form(), function(data, status) {
window.location.reload();
});
}
function add_synergy(obj) {
let firstEntityId = $(obj).attr("data-id");
let firstEntityType = 0;
let secondEntityId = $("#second_select_vals").val();
let secondEntityType = $("#second_select_types").val();
let probabilityValue = $("#synergy_prob_input").val();
let section = new URL(window.location.href).searchParams.get("section");
switch(section) {
case "agge":
firstEntityType = 0;
break;
case "hobb":
firstEntityType = 2;
break;
case "lugg":
firstEntityType = 3;
break;
case "heal":
firstEntityType = 1;
break;
case "prof":
firstEntityType = 4;
break;
}
let _set = 0;
switch(secondEntityType) {
case "agge":
_set = 0;
break;
case "hobb":
_set = 2;
break;
case "lugg":
_set = 3;
break;
case "heal":
_set = 1;
break;
case "prof":
_set = 4;
break;
}
secondEntityType = _set;
$.post("/api/add_synergy", "first_entity_id="+firstEntityId+"&first_entity_type="+firstEntityType+"&second_entity_id="+secondEntityId+"&second_entity_type="+secondEntityType+"&probability="+probabilityValue, function(data, status) {
window.location.reload();
});
}
function show_synergies_modal(obj) {
$("#add_synergy_button").attr("data-id", $(obj).attr("data-id"));
$(".table_row_generated").remove();
$.post("/api/get_synergies", "entity_id="+$(obj).attr("data-id")+"&entity_type="+new URL(window.location.href).searchParams.get("section"), function(data, status) {
data = JSON.parse(data);
for( let i = 0; i < data.length; i++ ) {
$("#table_body_modal").append("<tr class=\"table_row_generated\"><td>"+data[i].firstEntityText+"</td><td>"+data[i].firstType+"</td><td>"+data[i].secondEntityText+"</td><td>"+data[i].secondType+"</td><td>"+data[i].probabilityValue+"</td><td><button type=\"button\" class=\"btn btn-danger\" onclick=\"remove_synergy(this)\" data-synergy-id=\""+data[i].id+"\">Remove</button></td></tr>");
}
});
}
function secondTypeChange(obj) {
$.post("/api/get_entries", "section="+($(obj).val()), function(data, status) {
$("#second_select_vals").html("");
let jobj = JSON.parse(data);
for( let i = 0; i < jobj.length; i++ ) {
let ind = jobj[i];
let ss = 0;
if( ind.genderTextId != undefined ) {
ss = ind.genderTextId;
} else if( ind.textNameId != undefined ) {
ss = ind.textNameId;
} else if( ind.textDescId != undefined ) {
ss = ind.textDescId;
}
get_text_api(ss, function(rte) {
$("#second_select_vals").append($('<option>', {
value: ind.id,
text: rte
}));
});
}
});
}
function remove_synergy(obj) {
$.post("/api/remove_synergy", "synergy_id="+($(obj).attr("data-synergy-id")), function(data, status) {
window.location.reload();
});
}

View File

@ -0,0 +1,19 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/header}"></th:block>
</head>
<body>
<div th:replace="~{fragments/floating_button}"></div>
<div class="d-flex align-items-center justify-content-center vh-100">
<div class="text-center">
<h1 class="display-1 fw-bold" th:text="#{aaccden}"></h1>
<p class="fs-3"> <span class="text-danger" th:text="#{oopsi}"></span>[[#{badway}]]</p>
<a href="/" class="btn btn-primary" th:text="#{hhome}"></a>
</div>
</div>
<div th:replace="~{fragments/footer :: div}"></div>
</body>
</html>

View File

@ -0,0 +1,50 @@
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
<symbol id="check2" viewBox="0 0 16 16">
<path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/>
</symbol>
<symbol id="circle-half" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/>
</symbol>
<symbol id="moon-stars-fill" viewBox="0 0 16 16">
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"/>
<path d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z"/>
</symbol>
<symbol id="sun-fill" viewBox="0 0 16 16">
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"/>
</symbol>
</svg>
<div class="dropdown position-fixed bottom-0 end-0 mb-3 me-3 bd-mode-toggle">
<button class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center"
id="bd-theme"
type="button"
aria-expanded="false"
data-bs-toggle="dropdown"
aria-label="Toggle theme (auto)">
<svg class="bi my-1 theme-icon-active" width="1em" height="1em"><use href="#circle-half"></use></svg>
<span class="visually-hidden" id="bd-theme-text" th:text="#{toggleth}"></span>
</button>
<ul class="dropdown-menu dropdown-menu-end shadow" aria-labelledby="bd-theme-text">
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="light" aria-pressed="false">
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em"><use href="#sun-fill"></use></svg>
[[#{light}]]
<svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg>
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="dark" aria-pressed="false">
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em"><use href="#moon-stars-fill"></use></svg>
[[#{dark}]]
<svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg>
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center active" data-bs-theme-value="auto" aria-pressed="true">
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em"><use href="#circle-half"></use></svg>
[[#{auto}]]
<svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg>
</button>
</li>
</ul>
</div>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -0,0 +1,5 @@
<div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
</div>

View File

@ -0,0 +1,87 @@
<th:block th:fragment="head">
<script type="text/javascript" src="/static/javascript/color-modes.js"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title th:text="${title} ? ${title} : 'Bunker'"></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC">
<meta name="theme-color" content="#712cf9">
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
.b-example-divider {
width: 100%;
height: 3rem;
background-color: rgba(0, 0, 0, .1);
border: solid rgba(0, 0, 0, .15);
border-width: 1px 0;
box-shadow: inset 0 .5em 1.5em rgba(0, 0, 0, .1), inset 0 .125em .5em rgba(0, 0, 0, .15);
}
.b-example-vr {
flex-shrink: 0;
width: 1.5rem;
height: 100vh;
}
.bi {
vertical-align: -.125em;
fill: currentColor;
}
.nav-scroller {
position: relative;
z-index: 2;
height: 2.75rem;
overflow-y: hidden;
}
.nav-scroller .nav {
display: flex;
flex-wrap: nowrap;
padding-bottom: 1rem;
margin-top: -1px;
overflow-x: auto;
text-align: center;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
}
.btn-bd-primary {
--bd-violet-bg: #712cf9;
--bd-violet-rgb: 112.520718, 44.062154, 249.437846;
--bs-btn-font-weight: 600;
--bs-btn-color: var(--bs-white);
--bs-btn-bg: var(--bd-violet-bg);
--bs-btn-border-color: var(--bd-violet-bg);
--bs-btn-hover-color: var(--bs-white);
--bs-btn-hover-bg: #6528e0;
--bs-btn-hover-border-color: #6528e0;
--bs-btn-focus-shadow-rgb: var(--bd-violet-rgb);
--bs-btn-active-color: var(--bs-btn-hover-color);
--bs-btn-active-bg: #5a23c8;
--bs-btn-active-border-color: #5a23c8;
}
.bd-mode-toggle {
z-index: 1500;
}
.bd-mode-toggle .dropdown-menu .active .bi {
display: block !important;
}
</style>
</th:block>

View File

@ -0,0 +1,46 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/header}"></th:block>
<link rel="stylesheet" href="/static/css/util.css">
<link href="/static/css/sign-in.css" rel="stylesheet">
</head>
<body class="d-flex align-items-center py-4 bg-body-tertiary">
<div th:replace="~{fragments/floating_button}"></div>
<main class="form-signin w-100 m-auto">
<div th:if="${param.error}">
<div class="alert alert-danger" role="alert" th:text="#{login_error}">
</div>
</div>
<div th:if="${error}">
<div class="alert alert-danger" role="alert" th:text="#{login_error}">
</div>
</div>
<form method="post" th:action="@{/login}">
<h1 class="h3 mb-3 fw-normal" th:text="#{signin}"></h1>
<div class="form-floating">
<input type="text" name="username" class="form-control" id="floatingInput" placeholder="Username">
<label for="floatingInput" th:text="#{nname}"></label>
</div>
<div class="form-floating">
<input type="password" name="password" class="form-control" id="floatingPassword" placeholder="Password">
<label for="floatingPassword" th:text="#{pass}"></label>
</div>
<div class="form-check text-start my-3">
<input class="form-check-input" type="checkbox" value="remember-me" id="flexCheckDefault">
<label class="form-check-label" for="flexCheckDefault" th:text="#{remember}"></label>
</div>
<button class="btn btn-primary w-100 py-2" type="submit" th:text="#{signin}"></button>
<p class="mt-3"><a th:href="@{/signup}" class="link-primary" th:text="#{signup}"></a></p>
<p class="mb-3 text-body-secondary">&copy; alterdekim 2023</p>
</form>
</main>
<div th:replace="~{fragments/footer :: div}"></div>
</body>
</html>

View File

@ -0,0 +1,309 @@
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity6">
<head>
<th:block th:insert="~{fragments/header}"></th:block>
<link rel="stylesheet" href="/static/css/util.css">
<link rel="stylesheet" href="/static/css/panel.css">
</head>
<body class="bg-body-tertiary">
<div th:replace="~{fragments/floating_button}"></div>
<main>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark" aria-label="Navbar">
<div class="container-fluid">
<a class="navbar-brand" href="#">[[${title} ? ${title} : 'Bunker']]</a>
<button class="navbar-toggler" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasNavbar2" aria-controls="offcanvasNavbar2">
<span class="navbar-toggler-icon"></span>
</button>
<div class="offcanvas offcanvas-end text-bg-dark" tabindex="-1" id="offcanvasNavbar2" aria-labelledby="offcanvasNavbar2Label">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="offcanvasNavbar2Label">[[${title} ? ${title} : 'Bunker']]</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<ul class="navbar-nav justify-content-start flex-grow-1 pe-3">
<li class="nav-item">
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" th:text="#{pplay}" role="button" data-bs-toggle="dropdown" aria-expanded="false">
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" th:text="#{lugg}" href="/panel?section=lugg"></a></li>
<li><a class="dropdown-item" th:text="#{heeal}" href="/panel?section=heal"></a></li>
<li><a class="dropdown-item" th:text="#{pprof}" href="/panel?section=prof"></a></li>
<li><a class="dropdown-item" th:text="#{hhobb}" href="/panel?section=hobb"></a></li>
<li><a class="dropdown-item" th:text="#{aggend}" href="/panel?section=agge"></a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel?section=diss" th:text="#{diss}"></a>
</li>
<li class="nav-item">
<a class="nav-link" href="/panel?section=stats" th:text="#{stats}"></a>
</li>
</ul>
<ul class="navbar-nav justify-content-end flex-grow-1 pe-3">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-globe-americas" viewBox="0 0 16 16">
<path d="M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0ZM2.04 4.326c.325 1.329 2.532 2.54 3.717 3.19.48.263.793.434.743.484-.08.08-.162.158-.242.234-.416.396-.787.749-.758 1.266.035.634.618.824 1.214 1.017.577.188 1.168.38 1.286.983.082.417-.075.988-.22 1.52-.215.782-.406 1.48.22 1.48 1.5-.5 3.798-3.186 4-5 .138-1.243-2-2-3.5-2.5-.478-.16-.755.081-.99.284-.172.15-.322.279-.51.216-.445-.148-2.5-2-1.5-2.5.78-.39.952-.171 1.227.182.078.099.163.208.273.318.609.304.662-.132.723-.633.039-.322.081-.671.277-.867.434-.434 1.265-.791 2.028-1.12.712-.306 1.365-.587 1.579-.88A7 7 0 1 1 2.04 4.327Z"></path>
</svg>
[[#{langl}]]
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/?lang=en" th:text="#{elang}"></a></li>
<li><a class="dropdown-item" href="/?lang=ru" th:text="#{rlang}"></a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link" th:href="@{/logout}" th:text="#{logote}"></a>
</li>
</ul>
</div>
</div>
</div>
</nav>
<th:block th:if="${is_mobile}">
<div class="d-flex justify-content-center text-center">
<div class="row h-50">
<div class="card mt-2">
<div class="card-body">
<button type="button" data-bs-toggle="modal" onclick="add_entry_modal()" data-bs-target="#modal" class="btn btn-success" th:text="#{adddbtn}"></button>
</div>
</div>
<th:block th:each="cc: ${cards}">
<div class="card mt-2">
<div class="card-body">
<h5 class="card-title" th:text="${cc.title}"></h5>
<p class="card-text">
<ul th:each="ll: ${cc.body}">
<li th:text="${ll}"></li>
</ul>
</p>
<a href="#" th:data-id="${cc.id}" data-bs-toggle="modal" data-bs-target="#modal_syn" onclick="" class="btn btn-primary" th:text="#{eddittsynn}"></a>
<a href="#" th:data-id="${cc.id}" data-bs-toggle="modal" data-bs-target="#modal" onclick="edit_entry(this)" class="btn btn-primary" th:text="#{edditbtn}"></a>
<a href="#" th:data-id="${cc.id}" onclick="remove_entry(this)" class="btn btn-danger" th:text="#{remombtn}"></a>
</div>
</div>
</th:block>
<th:block th:if="${section == 'stats'}">
<p>stats!!!!</p>
</th:block>
</div>
</div>
</th:block>
<th:block th:unless="${is_mobile}">
<div class="d-flex justify-content-start text-center">
<div class="col">
<div class="card mt-2">
<div class="card-body">
<button type="button" data-bs-toggle="modal" onclick="add_entry_modal()" data-bs-target="#modal" class="btn btn-success" th:text="#{adddbtn}"></button>
</div>
</div>
</div>
<th:block th:each="cc: ${cards}">
<div class="col">
<div class="card mt-2">
<div class="card-body">
<h5 class="card-title" th:text="${cc.title}"></h5>
<p class="card-text">
<ul th:each="ll: ${cc.body}">
<li th:text="${ll}"></li>
</ul>
</p>
<a href="#" th:data-id="${cc.id}" data-bs-toggle="modal" data-bs-target="#modal_syn" onclick="show_synergies_modal(this)" class="btn btn-primary" th:text="#{eddittsynn}"></a>
<a href="#" th:data-id="${cc.id}" data-bs-toggle="modal" data-bs-target="#modal" onclick="edit_entry(this)" class="btn btn-primary" th:text="#{edditbtn}"></a>
<a href="#" th:data-id="${cc.id}" onclick="remove_entry(this)" class="btn btn-danger" th:text="#{remombtn}"></a>
</div>
</div>
</div>
</th:block>
</div>
</th:block>
<div class="modal fade" id="modal_syn" tabindex="-1" aria-labelledby="ModalLabelSyn" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="ModalLabelSyn" th:text="#{aedentry}"></h1>
</div>
<div class="modal-body">
<form id="synergyForm">
<table class="table">
<thead>
<tr>
<th th:text="#{firentname}"></th>
<th th:text="#{firenttype}"></th>
<th th:text="#{secentname}"></th>
<th th:text="#{secenttype}"></th>
<th th:text="#{probbval}"></th>
<th th:text="#{actionbtn}"></th>
</tr>
</thead>
<tbody id="table_body_modal">
<tr>
<td>-</td>
<td>-</td>
<td><select id="second_select_vals"></select></td>
<td><select id="second_select_types" name="second_types" onchange="secondTypeChange(this)"><option value="lugg" th:text="#{lugg}"></option><option value="heal" th:text="#{heeal}"></option><option value="hobb" th:text="#{hhobb}"></option><option value="agge" th:text="#{aggend}">Gender</option><option value="prof" th:text="#{pprof}">Profession</option></select></td>
<td><input id="synergy_prob_input" type="number" value="0.0" min="-1" max="1" step="0.1"/></td>
<td><button type="button" data-id="0" id="add_synergy_button" class="btn btn-primary" onclick="add_synergy(this)" th:text="#{adddbtn}"></button></td>
</tr>
</tbody>
</table>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-toggle="modal" data-bs-dismiss="modal" th:text="#{cancbt}"></button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="modal" tabindex="-1" data-bs-backdrop="static" aria-labelledby="ModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="ModalLabel" th:text="#{aedentry}"></h1>
</div>
<div class="modal-body">
<form id="entryForm">
<th:block th:switch="${section}">
<th:block th:case="agge">
<div class="mb-3">
<label class="form-check-label" th:text="#{inlcandie}" for="canDie"></label>
<input class="form-check-input" type="checkbox" value="" id="canDie" name="canDie" checked>
</div>
<div class="mb-3">
<label class="form-check-label" for="ismale" th:text="#{inlismal}"></label>
<input class="form-check-input" type="checkbox" value="" id="ismale" name="ismale">
</div>
<div class="mb-3">
<label class="form-check-label" for="isfemale" th:text="#{inlisfem}"></label>
<input class="form-check-input" type="checkbox" value="" id="isfemale" name="isfemale" checked>
</div>
<div class="mb-3">
<label for="gender_text" class="col-form-label" th:text="#{inlgente}"></label>
<input type="text" class="form-control" id="gender_text" name="gender_text">
</div>
</th:block>
<th:block th:case="hobb">
<div class="mb-3">
<label for="foodRange" class="form-label" th:text="#{inlfood}"></label>
<input type="range" class="form-range" id="foodRange" min="0" max="1" step="0.1" name="foodRange">
</div>
<div class="mb-3">
<label for="healRange" class="form-label" th:text="#{inlheal}"></label>
<input type="range" class="form-range" id="healRange" min="0" max="1" step="0.1" name="healRange">
</div>
<div class="mb-3">
<label for="powerRange" class="form-label" th:text="#{inlpower}"></label>
<input type="range" class="form-range" id="powerRange" min="0" max="1" step="0.1" name="powerRange">
</div>
<div class="mb-3">
<label for="violenceRange" class="form-label" th:text="#{inlviol}"></label>
<input type="range" class="form-range" id="violenceRange" min="0" max="1" step="0.1" name="violenceRange">
</div>
<div class="mb-3">
<label for="hobby_text" class="col-form-label" th:text="#{hobblab}"></label>
<input type="text" class="form-control" id="hobby_text" name="hobby_text">
</div>
</th:block>
<th:block th:case="lugg">
<div class="mb-3">
<label for="foodRange" class="form-label" th:text="#{inlfood}"></label>
<input type="range" class="form-range" id="foodRange" min="0" max="1" step="0.1" name="foodRange">
</div>
<div class="mb-3">
<label for="healRange" class="form-label" th:text="#{inlheal}"></label>
<input type="range" class="form-range" id="healRange" min="0" max="1" step="0.1" name="healRange">
</div>
<div class="mb-3">
<label for="powerRange" class="form-label" th:text="#{inlpower}"></label>
<input type="range" class="form-range" id="powerRange" min="0" max="1" step="0.1" name="powerRange">
</div>
<div class="mb-3">
<label for="violenceRange" class="form-label" th:text="#{inlviol}"></label>
<input type="range" class="form-range" id="violenceRange" min="0" max="1" step="0.1" name="violenceRange">
</div>
<div class="mb-3">
<label class="form-check-label" for="isgarbage" th:text="#{inlgarb}"></label>
<input class="form-check-input" type="checkbox" value="" id="isgarbage" name="isgarbage">
</div>
<div class="mb-3">
<label for="luggage_name_text" class="col-form-label" th:text="#{inlaname}"></label>
<input type="text" class="form-control" id="luggage_name_text" name="luggage_name_text">
</div>
<div class="mb-3">
<label for="luggage_desc_text" class="col-form-label" th:text="#{inladesc}"></label>
<input type="text" class="form-control" id="luggage_desc_text" name="luggage_desc_text">
</div>
</th:block>
<th:block th:case="heal">
<div class="mb-3">
<label for="health_index" class="form-label" th:text="#{inlhin}"></label>
<input type="range" class="form-range" id="health_index" min="0" max="1" step="0.1" name="health_index">
</div>
<div class="mb-3">
<label class="form-check-label" for="childFree" th:text="#{inlcfr}"></label>
<input class="form-check-input" type="checkbox" value="" id="childFree" name="childFree">
</div>
<div class="mb-3">
<label for="heal_name_text" class="col-form-label" th:text="#{inlaname}"></label>
<input type="text" class="form-control" id="heal_name_text" name="heal_name_text">
</div>
<div class="mb-3">
<label for="heal_desc_text" class="col-form-label" th:text="#{inladesc}"></label>
<input type="text" class="form-control" id="heal_desc_text" name="heal_desc_text">
</div>
</th:block>
<th:block th:case="prof">
<div class="mb-3">
<label for="foodRange" class="form-label" th:text="#{inlfood}"></label>
<input type="range" class="form-range" id="foodRange" min="0" max="1" step="0.1" name="foodRange">
</div>
<div class="mb-3">
<label for="healRange" class="form-label" th:text="#{inlheal}"></label>
<input type="range" class="form-range" id="healRange" min="0" max="1" step="0.1" name="healRange">
</div>
<div class="mb-3">
<label for="powerRange" class="form-label" th:text="#{inlpower}"></label>
<input type="range" class="form-range" id="powerRange" min="0" max="1" step="0.1" name="powerRange">
</div>
<div class="mb-3">
<label for="violenceRange" class="form-label" th:text="#{inlviol}"></label>
<input type="range" class="form-range" id="violenceRange" min="0" max="1" step="0.1" name="violenceRange">
</div>
<div class="mb-3">
<label for="work_name_text" class="col-form-label" th:text="#{inlaname}"></label>
<input type="text" class="form-control" id="work_name_text" name="work_name_text">
</div>
<div class="mb-3">
<label for="work_desc_text" class="col-form-label" th:text="#{inladesc}"></label>
<input type="text" class="form-control" id="work_desc_text" name="work_desc_text">
</div>
</th:block>
<th:block th:case="diss">
<div class="mb-3">
<label for="diss_name_text" class="col-form-label" th:text="#{inlaname}"></label>
<input type="text" class="form-control" id="diss_name_text" name="diss_name_text">
</div>
<div class="mb-3">
<label for="diss_desc_text" class="col-form-label" th:text="#{inladesc}"></label>
<input type="text" class="form-control" id="diss_desc_text" name="diss_desc_text">
</div>
</th:block>
</th:block>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" th:text="#{cancbt}"></button>
<button type="button" id="modal_submit_add" onclick="add_entry()" class="btn btn-success" th:text="#{adddbtn}"></button>
<button type="button" data-entry-id="0" style="display: none;" id="modal_submit_edit" onclick="edit_submit_entry(this)" class="btn btn-primary" th:text="#{edditbtn}"></button>
</div>
</div>
</div>
</div>
<script src="/static/javascript/panel-script.js" type="text/javascript"></script>
</main>
<div th:replace="~{fragments/footer :: div}"></div>
</body>
</html>

View File

@ -0,0 +1,51 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/header}"></th:block>
<link rel="stylesheet" href="/static/css/util.css">
<link href="/static/css/sign-up.css" rel="stylesheet">
</head>
<body class="d-flex align-items-center py-4 bg-body-tertiary">
<div th:replace="~{fragments/floating_button}"></div>
<main class="form-signup w-100 m-auto">
<div th:if="${param.error}">
<div class="alert alert-danger" role="alert" th:text="#{incorcred}">
</div>
</div>
<div th:if="${error}">
<div class="alert alert-danger" role="alert" th:text="#{incorcred}">
</div>
</div>
<form method="post" th:action="@{/signup}" th:object="${user}">
<h1 class="h3 mb-3 fw-normal" th:text="#{signup}"></h1>
<div class="form-floating">
<input type="text" name="username" class="form-control" id="floatingInput" placeholder="Username">
<label for="floatingInput" th:text="#{nname}"></label>
</div>
<div class="form-floating mt-1">
<input type="password" name="password" class="form-control" id="floatingPassword" placeholder="Password">
<label for="floatingPassword" th:text="#{pass}"></label>
</div>
<div class="form-floating mt-1">
<input type="text" name="invite_code" class="form-control" id="floatingInvite" placeholder="Invite code">
<label for="floatingInvite" th:text="#{invitecode}"></label>
</div>
<button class="btn btn-primary w-100 py-2 mt-3" type="submit" th:text="#{signup}"></button>
<p class="mt-3"><a th:href="@{/login}" class="link-primary" th:text="#{signin}"></a></p>
<p class="mb-3 text-body-secondary">&copy; alterdekim 2023</p>
</form>
</main>
<div th:replace="~{fragments/footer :: div}"></div>
</body>
</html>