diff --git a/.gitignore b/.gitignore index a80f6e9ed..ec937c61d 100644 --- a/.gitignore +++ b/.gitignore @@ -141,3 +141,8 @@ gc-*.log .gradle/* */out/* VERSION.txt +fredboat.yaml +fredboat.yml +application.yaml +application.yml +backend.yaml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 31a8c10a0..62e26ba90 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -115,7 +115,7 @@ Obtaining a well functioning and productive setup of the IDE may be an intimidat ### Running the bot -Add `credentials.yaml` and `config.yaml` files to the FredBoat root directory. +Add the `fredboat.yaml` file to the FredBoat root directory (symlinking recommended). To run the FredBoat bot in IntelliJ IDEA, find the little green play button in the main class `FredBoat.java` and start it from there:
Click me diff --git a/Database/build.gradle b/Database/build.gradle index f61294985..ee8c7f08b 100644 --- a/Database/build.gradle +++ b/Database/build.gradle @@ -3,6 +3,31 @@ version '1.0' ext { moduleName = 'Database' } + +apply plugin: 'maven-publish' + +publishing { + publications { + mavenJava(MavenPublication) { + groupId rootProject.group + artifactId moduleName + + from components.java + + artifact sourceJar { + classifier "sources" + } + } + } +} + +task install(dependsOn: 'publishToMavenLocal') +publishToMavenLocal.dependsOn 'jar' + +task sourceJar(type: Jar) { + from sourceSets.main.allJava +} + dependencies { compile project(':Shared') @@ -14,4 +39,7 @@ dependencies { compile group: 'org.hibernate', name: 'hibernate-ehcache', version: hibernateVersion compile group: 'org.flywaydb', name: 'flyway-core', version: flywayVersion compile group: 'net.ttddyy', name: 'datasource-proxy', version: dsProxyVersion + compile group: 'javax.xml.bind', name: 'jaxb-api', version: jaxbApiVersion // required by hibernate for java 9 + + compile group: 'com.google.code.gson', name: 'gson', version: gsonVersion } diff --git a/Database/src/main/java/fredboat/db/DatabaseManager.java b/Database/src/main/java/fredboat/db/DatabaseManager.java index 23c5b2780..f96f26dee 100644 --- a/Database/src/main/java/fredboat/db/DatabaseManager.java +++ b/Database/src/main/java/fredboat/db/DatabaseManager.java @@ -29,8 +29,6 @@ import com.zaxxer.hikari.metrics.prometheus.PrometheusMetricsTrackerFactory; import io.prometheus.client.hibernate.HibernateStatisticsCollector; import net.sf.ehcache.CacheManager; -import net.sf.ehcache.config.CacheConfiguration; -import net.sf.ehcache.config.PersistenceConfiguration; import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.flywaydb.core.Flyway; @@ -40,7 +38,6 @@ import space.npstr.sqlsauce.DatabaseConnection; import space.npstr.sqlsauce.DatabaseException; import space.npstr.sqlsauce.DatabaseWrapper; -import space.npstr.sqlsauce.ssh.SshTunnel; import javax.annotation.Nullable; import java.util.Properties; @@ -62,12 +59,8 @@ public class DatabaseManager { private final boolean migrateAndValidate; private final String mainJdbc; @Nullable - private final SshTunnel.SshDetails mainTunnel; - @Nullable private final String cacheJdbc; @Nullable - private final SshTunnel.SshDetails cacheTunnel; - @Nullable private final DatabaseConnection.EntityManagerFactoryBuilder entityManagerFactoryBuilder; @Nullable @@ -92,9 +85,7 @@ public DatabaseManager(@Nullable HibernateStatisticsCollector hibernateStats, String appName, boolean migrateAndValidate, String mainJdbc, - @Nullable SshTunnel.SshDetails mainTunnel, @Nullable String cacheJdbc, - @Nullable SshTunnel.SshDetails cacheTunnel, @Nullable DatabaseConnection.EntityManagerFactoryBuilder entityManagerFactoryBuilder) { this.hibernateStats = hibernateStats; this.hikariStats = hikariStats; @@ -102,10 +93,15 @@ public DatabaseManager(@Nullable HibernateStatisticsCollector hibernateStats, this.appName = appName; this.migrateAndValidate = migrateAndValidate; this.mainJdbc = mainJdbc; - this.mainTunnel = mainTunnel; this.cacheJdbc = cacheJdbc; - this.cacheTunnel = cacheTunnel; this.entityManagerFactoryBuilder = entityManagerFactoryBuilder; + + if (mainJdbc.isEmpty()) { + log.error("Main jdbc url is empty - database creation will fail"); + } + if (cacheJdbc != null && cacheJdbc.isEmpty()) { + log.error("Cache jdbc url is empty - database creation will fail"); + } } public DatabaseConnection getMainDbConn() { @@ -138,7 +134,7 @@ public DatabaseWrapper getMainDbWrapper() { @Nullable //may return null if no cache db is configured public DatabaseConnection getCacheDbConn() { - if (cacheJdbc == null) { + if (cacheJdbc == null || cacheJdbc.isEmpty()) { return null; } DatabaseConnection singleton = cacheDbConn; @@ -192,15 +188,9 @@ private DatabaseConnection initMainDbConn() throws DatabaseException { DatabaseConnection databaseConnection = getBasicConnectionBuilder(MAIN_PERSISTENCE_UNIT_NAME, mainJdbc) .setHibernateProps(buildHibernateProps("ehcache_main.xml")) .addEntityPackage("fredboat.db.entity.main") - .setSshDetails(mainTunnel) .setFlyway(flyway) .build(); - //adjusting the ehcache config - if (mainTunnel == null && cacheTunnel == null) { - //local database: turn off overflow to disk of the cache - turnOffLocalStorageForEhcacheManager("MAIN_CACHEMANAGER"); - } log.debug(CacheManager.getCacheManager("MAIN_CACHEMANAGER").getActiveConfigurationText()); return databaseConnection; @@ -217,15 +207,9 @@ public DatabaseConnection initCacheConn(String jdbc) throws DatabaseException { DatabaseConnection databaseConnection = getBasicConnectionBuilder(CACHE_PERSISTENCE_UNIT_NAME, jdbc) .setHibernateProps(buildHibernateProps("ehcache_cache.xml")) .addEntityPackage("fredboat.db.entity.cache") - .setSshDetails(cacheTunnel) .setFlyway(flyway) .build(); - //adjusting the ehcache config - if (mainTunnel == null && cacheTunnel == null) { - //local database: turn off overflow to disk of the cache - turnOffLocalStorageForEhcacheManager("CACHE_CACHEMANAGER"); - } log.debug(CacheManager.getCacheManager("CACHE_CACHEMANAGER").getActiveConfigurationText()); return databaseConnection; @@ -283,12 +267,4 @@ private Properties buildHibernateProps(String ehcacheXmlFile) { return hibernateProps; } - - private void turnOffLocalStorageForEhcacheManager(String cacheManagerName) { - CacheManager cacheManager = CacheManager.getCacheManager(cacheManagerName); - for (String cacheName : cacheManager.getCacheNames()) { - CacheConfiguration cacheConfig = cacheManager.getCache(cacheName).getCacheConfiguration(); - cacheConfig.getPersistenceConfiguration().strategy(PersistenceConfiguration.Strategy.NONE); - } - } } diff --git a/Database/src/main/java/fredboat/db/api/BlacklistIO.java b/Database/src/main/java/fredboat/db/api/BlacklistService.java similarity index 97% rename from Database/src/main/java/fredboat/db/api/BlacklistIO.java rename to Database/src/main/java/fredboat/db/api/BlacklistService.java index 4580d8653..5d691b426 100644 --- a/Database/src/main/java/fredboat/db/api/BlacklistIO.java +++ b/Database/src/main/java/fredboat/db/api/BlacklistService.java @@ -32,7 +32,7 @@ /** * Created by napster on 07.02.18. */ -public interface BlacklistIO { +public interface BlacklistService { /** * @return the whole blacklist aka all entries. Not a lightweight operation, and shouldn't be called outside diff --git a/Database/src/main/java/fredboat/db/api/GuildConfigIO.java b/Database/src/main/java/fredboat/db/api/GuildConfigService.java similarity index 97% rename from Database/src/main/java/fredboat/db/api/GuildConfigIO.java rename to Database/src/main/java/fredboat/db/api/GuildConfigService.java index f2804cdfa..5596e6bc0 100644 --- a/Database/src/main/java/fredboat/db/api/GuildConfigIO.java +++ b/Database/src/main/java/fredboat/db/api/GuildConfigService.java @@ -33,7 +33,7 @@ /** * Created by napster on 07.02.18. */ -public interface GuildConfigIO { +public interface GuildConfigService { GuildConfig fetchGuildConfig(Guild guild); diff --git a/Database/src/main/java/fredboat/db/api/GuildDataIO.java b/Database/src/main/java/fredboat/db/api/GuildDataService.java similarity index 97% rename from Database/src/main/java/fredboat/db/api/GuildDataIO.java rename to Database/src/main/java/fredboat/db/api/GuildDataService.java index af8f1fc0a..f3050a665 100644 --- a/Database/src/main/java/fredboat/db/api/GuildDataIO.java +++ b/Database/src/main/java/fredboat/db/api/GuildDataService.java @@ -33,7 +33,7 @@ /** * Created by napster on 07.02.18. */ -public interface GuildDataIO { +public interface GuildDataService { GuildData fetchGuildData(Guild guild); diff --git a/Database/src/main/java/fredboat/db/api/GuildModulesIO.java b/Database/src/main/java/fredboat/db/api/GuildModulesService.java similarity index 97% rename from Database/src/main/java/fredboat/db/api/GuildModulesIO.java rename to Database/src/main/java/fredboat/db/api/GuildModulesService.java index 530c96115..2273e2168 100644 --- a/Database/src/main/java/fredboat/db/api/GuildModulesIO.java +++ b/Database/src/main/java/fredboat/db/api/GuildModulesService.java @@ -33,7 +33,7 @@ /** * Created by napster on 07.02.18. */ -public interface GuildModulesIO { +public interface GuildModulesService { GuildModules fetchGuildModules(Guild guild); diff --git a/Database/src/main/java/fredboat/db/api/GuildPermsIO.java b/Database/src/main/java/fredboat/db/api/GuildPermsService.java similarity index 97% rename from Database/src/main/java/fredboat/db/api/GuildPermsIO.java rename to Database/src/main/java/fredboat/db/api/GuildPermsService.java index e1befd97f..6604b8ea4 100644 --- a/Database/src/main/java/fredboat/db/api/GuildPermsIO.java +++ b/Database/src/main/java/fredboat/db/api/GuildPermsService.java @@ -33,7 +33,7 @@ /** * Created by napster on 07.02.18. */ -public interface GuildPermsIO { +public interface GuildPermsService { GuildPermissions fetchGuildPermissions(Guild guild); diff --git a/Database/src/main/java/fredboat/db/api/PrefixIO.java b/Database/src/main/java/fredboat/db/api/PrefixService.java similarity index 97% rename from Database/src/main/java/fredboat/db/api/PrefixIO.java rename to Database/src/main/java/fredboat/db/api/PrefixService.java index 15b993243..25d3476f9 100644 --- a/Database/src/main/java/fredboat/db/api/PrefixIO.java +++ b/Database/src/main/java/fredboat/db/api/PrefixService.java @@ -35,7 +35,7 @@ /** * Created by napster on 07.02.18. */ -public interface PrefixIO { +public interface PrefixService { Prefix transformPrefix(Guild guild, Function transformation); diff --git a/Database/src/main/java/fredboat/db/api/SearchResultIO.java b/Database/src/main/java/fredboat/db/api/SearchResultService.java similarity index 97% rename from Database/src/main/java/fredboat/db/api/SearchResultIO.java rename to Database/src/main/java/fredboat/db/api/SearchResultService.java index b40f45c3e..386bc71ff 100644 --- a/Database/src/main/java/fredboat/db/api/SearchResultIO.java +++ b/Database/src/main/java/fredboat/db/api/SearchResultService.java @@ -32,7 +32,7 @@ /** * Created by napster on 07.02.18. */ -public interface SearchResultIO { +public interface SearchResultService { /** * Merge a search result into the database. diff --git a/Database/src/main/java/fredboat/db/entity/cache/SearchResult.java b/Database/src/main/java/fredboat/db/entity/cache/SearchResult.java index deae5b409..069f9b3d9 100644 --- a/Database/src/main/java/fredboat/db/entity/cache/SearchResult.java +++ b/Database/src/main/java/fredboat/db/entity/cache/SearchResult.java @@ -190,6 +190,11 @@ public boolean equals(Object o) { SearchResultId other = (SearchResultId) o; return provider.equals(other.provider) && searchTerm.equals(other.searchTerm); } + + @Override + public String toString() { + return "Search: Provider " + provider + " Term " + searchTerm; + } } diff --git a/Database/src/main/java/fredboat/db/repositories/api/GuildBasedRepo.java b/Database/src/main/java/fredboat/db/repositories/api/GuildBasedRepo.java index ef4096f19..a692250a9 100644 --- a/Database/src/main/java/fredboat/db/repositories/api/GuildBasedRepo.java +++ b/Database/src/main/java/fredboat/db/repositories/api/GuildBasedRepo.java @@ -27,30 +27,20 @@ import net.dv8tion.jda.core.entities.Guild; -import javax.annotation.Nullable; - /** * Created by napster on 05.02.18. */ public interface GuildBasedRepo extends Repo { /** - * Type safety on top of {@link Repo#get(Object)} for entities based on guilds. - */ - @Nullable - default E get(Guild guild) { - return get(guild.getIdLong()); - } - - /** - * Type safety on top of {@link Repo#get(Object)} for entities based on guilds. + * Type safety on top of {@link Repo#delete(Object)} for entities based on guilds. */ default void delete(Guild guild) { delete(guild.getIdLong()); } /** - * Type safety on top of {@link Repo#get(Object)} for entities based on guilds. + * Type safety on top of {@link Repo#fetch(Object)} for entities based on guilds. */ default E fetch(Guild guild) { return fetch(guild.getIdLong()); diff --git a/Database/src/main/java/fredboat/db/repositories/api/LegacyGuildBasedRepo.java b/Database/src/main/java/fredboat/db/repositories/api/LegacyGuildBasedRepo.java index c57dbce47..39a43d03e 100644 --- a/Database/src/main/java/fredboat/db/repositories/api/LegacyGuildBasedRepo.java +++ b/Database/src/main/java/fredboat/db/repositories/api/LegacyGuildBasedRepo.java @@ -27,8 +27,6 @@ import net.dv8tion.jda.core.entities.Guild; -import javax.annotation.Nullable; - /** * Created by napster on 05.02.18. *

@@ -38,22 +36,14 @@ public interface LegacyGuildBasedRepo extends Repo { /** - * Type safety on top of {@link Repo#get(Object)} for entities based on guilds. - */ - @Nullable - default E get(Guild guild) { - return get(guild.getId()); - } - - /** - * Type safety on top of {@link Repo#get(Object)} for entities based on guilds. + * Type safety on top of {@link Repo#delete(Object)} for entities based on guilds. */ default void delete(Guild guild) { delete(guild.getId()); } /** - * Type safety on top of {@link Repo#get(Object)} for entities based on guilds. + * Type safety on top of {@link Repo#fetch(Object)} for entities based on guilds. */ default E fetch(Guild guild) { return fetch(guild.getId()); diff --git a/Database/src/main/java/fredboat/db/repositories/api/Repo.java b/Database/src/main/java/fredboat/db/repositories/api/Repo.java index 3bdb5c1c1..b6e19e33d 100644 --- a/Database/src/main/java/fredboat/db/repositories/api/Repo.java +++ b/Database/src/main/java/fredboat/db/repositories/api/Repo.java @@ -25,20 +25,11 @@ package fredboat.db.repositories.api; -import javax.annotation.Nullable; - /** * Created by napster on 05.02.18. */ public interface Repo { - /** - * @param id id of the entity that shall be returned - * @return the entity of the provided id, if such an entity exists in the database, null otherwise - */ - @Nullable - E get(I id); - /** * @param id of the entity that shall be deleted */ diff --git a/Database/src/main/java/fredboat/db/repositories/impl/SqlSauceRepo.java b/Database/src/main/java/fredboat/db/repositories/impl/SqlSauceRepo.java index 21fb88252..59fa94598 100644 --- a/Database/src/main/java/fredboat/db/repositories/impl/SqlSauceRepo.java +++ b/Database/src/main/java/fredboat/db/repositories/impl/SqlSauceRepo.java @@ -30,7 +30,6 @@ import space.npstr.sqlsauce.entities.SaucedEntity; import space.npstr.sqlsauce.fp.types.EntityKey; -import javax.annotation.Nullable; import java.io.Serializable; /** @@ -54,12 +53,6 @@ public Class getEntityClass() { return entityClass; } - @Nullable - @Override - public E get(I id) { - return dbWrapper.getEntity(EntityKey.of(id, entityClass)); - } - @Override public void delete(I id) { dbWrapper.deleteEntity(EntityKey.of(id, entityClass)); diff --git a/Database/src/main/java/fredboat/db/repositories/impl/rest/BackendException.java b/Database/src/main/java/fredboat/db/repositories/impl/rest/BackendException.java new file mode 100644 index 000000000..12bd10f66 --- /dev/null +++ b/Database/src/main/java/fredboat/db/repositories/impl/rest/BackendException.java @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fredboat.db.repositories.impl.rest; + +/** + * Created by napster on 28.02.18. + *

+ * Usually wraps another exception so look for the cause. + */ +public class BackendException extends RuntimeException { + + private static final long serialVersionUID = -2936856678286772706L; + + public BackendException() {//force creation with a message / cause + } + + public BackendException(String message) { + super(message); + } + + public BackendException(String message, Throwable cause) { + super(message, cause); + } + + public BackendException(Throwable cause) { + super(cause); + } +} diff --git a/Database/src/main/java/fredboat/db/repositories/impl/rest/CachedRestRepo.java b/Database/src/main/java/fredboat/db/repositories/impl/rest/CachedRestRepo.java new file mode 100644 index 000000000..c0ffe0109 --- /dev/null +++ b/Database/src/main/java/fredboat/db/repositories/impl/rest/CachedRestRepo.java @@ -0,0 +1,91 @@ +/* + * + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fredboat.db.repositories.impl.rest; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.gson.Gson; +import fredboat.util.rest.CacheUtil; +import fredboat.util.rest.Http; +import io.prometheus.client.guava.cache.CacheMetricsCollector; +import space.npstr.sqlsauce.entities.SaucedEntity; + +import java.io.Serializable; +import java.util.concurrent.TimeUnit; + +/** + * Created by napster on 18.02.18. + */ +public abstract class CachedRestRepo> extends RestRepo { + + private final LoadingCache cache; + + /** + * Create the CachedRestRepo using a default cache + */ + public CachedRestRepo(String path, Class entityClass, Http http, Gson gson, String auth) { + this(path, entityClass, http, gson, auth, + CacheBuilder.newBuilder() + .expireAfterAccess(60, TimeUnit.SECONDS) + .expireAfterWrite(120, TimeUnit.SECONDS) + .recordStats() + ); + } + + public CachedRestRepo(String path, Class entityClass, Http http, Gson gson, String auth, CacheBuilder cacheBuilder) { + super(path, entityClass, http, gson, auth); + this.cache = cacheBuilder.build(CacheLoader.from(super::fetch)); + } + + /** + * Add the cache of this CachedRestRepo to the provided metrics under the provided name + * + * @return itself for chaining calls + */ + public CachedRestRepo registerCacheStats(CacheMetricsCollector cacheMetrics, String name) { + cacheMetrics.addCache(name, cache); + return this; + } + + @Override + public void delete(I id) { + super.delete(id); + cache.invalidate(id); + } + + @Override + public E fetch(I id) { + return CacheUtil.getUncheckedUnwrapped(cache, id); + } + + @Override + public E merge(E entity) { + E merged = super.merge(entity); + cache.put(merged.getId(), merged); + return merged; + } +} diff --git a/Database/src/main/java/fredboat/db/repositories/impl/rest/RestBlacklistRepo.java b/Database/src/main/java/fredboat/db/repositories/impl/rest/RestBlacklistRepo.java new file mode 100644 index 000000000..1a98d3c8e --- /dev/null +++ b/Database/src/main/java/fredboat/db/repositories/impl/rest/RestBlacklistRepo.java @@ -0,0 +1,69 @@ +/* + * + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fredboat.db.repositories.impl.rest; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; +import fredboat.db.entity.main.BlacklistEntry; +import fredboat.db.repositories.api.BlacklistRepo; +import fredboat.util.rest.Http; +import io.prometheus.client.guava.cache.CacheMetricsCollector; + +import java.io.IOException; +import java.util.List; + +/** + * Created by napster on 17.02.18. + */ +public class RestBlacklistRepo extends CachedRestRepo implements BlacklistRepo { + + public static final String PATH = "blacklist/"; + + public RestBlacklistRepo(String apiBasePath, Http http, Gson gson, String auth) { + super(apiBasePath + VERSION_PATH + PATH, BlacklistEntry.class, http, gson, auth); + } + + + @Override + public RestBlacklistRepo registerCacheStats(CacheMetricsCollector cacheMetrics, String name) { + super.registerCacheStats(cacheMetrics, name); + return this; + } + + @Override + public List loadBlacklist() { + try { + Http.SimpleRequest get = http.get(path + "loadall"); + String answer = auth(get).asString(); + log.debug(answer); + return gson.fromJson(answer, new TypeToken>() { + }.getType()); + } catch (IOException | JsonSyntaxException e) { + throw new BackendException("Could not load the blacklist", e); + } + } +} diff --git a/Database/src/main/java/fredboat/db/repositories/impl/rest/RestGuildConfigRepo.java b/Database/src/main/java/fredboat/db/repositories/impl/rest/RestGuildConfigRepo.java new file mode 100644 index 000000000..9dc05a753 --- /dev/null +++ b/Database/src/main/java/fredboat/db/repositories/impl/rest/RestGuildConfigRepo.java @@ -0,0 +1,50 @@ +/* + * + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fredboat.db.repositories.impl.rest; + +import com.google.gson.Gson; +import fredboat.db.entity.main.GuildConfig; +import fredboat.db.repositories.api.GuildConfigRepo; +import fredboat.util.rest.Http; +import io.prometheus.client.guava.cache.CacheMetricsCollector; + +/** + * Created by napster on 17.02.18. + */ +public class RestGuildConfigRepo extends CachedRestRepo implements GuildConfigRepo { + + public static final String PATH = "guildconfig/"; + + public RestGuildConfigRepo(String apiBasePath, Http http, Gson gson, String auth) { + super(apiBasePath + VERSION_PATH + PATH, GuildConfig.class, http, gson, auth); + } + + @Override + public RestGuildConfigRepo registerCacheStats(CacheMetricsCollector cacheMetrics, String name) { + super.registerCacheStats(cacheMetrics, name); + return this; + } +} diff --git a/Database/src/main/java/fredboat/db/repositories/impl/rest/RestGuildDataRepo.java b/Database/src/main/java/fredboat/db/repositories/impl/rest/RestGuildDataRepo.java new file mode 100644 index 000000000..db54b7af8 --- /dev/null +++ b/Database/src/main/java/fredboat/db/repositories/impl/rest/RestGuildDataRepo.java @@ -0,0 +1,50 @@ +/* + * + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fredboat.db.repositories.impl.rest; + +import com.google.gson.Gson; +import fredboat.db.entity.main.GuildData; +import fredboat.db.repositories.api.GuildDataRepo; +import fredboat.util.rest.Http; +import io.prometheus.client.guava.cache.CacheMetricsCollector; + +/** + * Created by napster on 17.02.18. + */ +public class RestGuildDataRepo extends CachedRestRepo implements GuildDataRepo { + + public static final String PATH = "guilddata/"; + + public RestGuildDataRepo(String apiBasePath, Http http, Gson gson, String auth) { + super(apiBasePath + VERSION_PATH + PATH, GuildData.class, http, gson, auth); + } + + @Override + public RestGuildDataRepo registerCacheStats(CacheMetricsCollector cacheMetrics, String name) { + super.registerCacheStats(cacheMetrics, name); + return this; + } +} diff --git a/Database/src/main/java/fredboat/db/repositories/impl/rest/RestGuildModulesRepo.java b/Database/src/main/java/fredboat/db/repositories/impl/rest/RestGuildModulesRepo.java new file mode 100644 index 000000000..f072c6824 --- /dev/null +++ b/Database/src/main/java/fredboat/db/repositories/impl/rest/RestGuildModulesRepo.java @@ -0,0 +1,50 @@ +/* + * + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fredboat.db.repositories.impl.rest; + +import com.google.gson.Gson; +import fredboat.db.entity.main.GuildModules; +import fredboat.db.repositories.api.GuildModulesRepo; +import fredboat.util.rest.Http; +import io.prometheus.client.guava.cache.CacheMetricsCollector; + +/** + * Created by napster on 17.02.18. + */ +public class RestGuildModulesRepo extends CachedRestRepo implements GuildModulesRepo { + + public static final String PATH = "guildmodules/"; + + public RestGuildModulesRepo(String apiBasePath, Http http, Gson gson, String auth) { + super(apiBasePath + VERSION_PATH + PATH, GuildModules.class, http, gson, auth); + } + + @Override + public RestGuildModulesRepo registerCacheStats(CacheMetricsCollector cacheMetrics, String name) { + super.registerCacheStats(cacheMetrics, name); + return this; + } +} diff --git a/Database/src/main/java/fredboat/db/repositories/impl/rest/RestGuildPermsRepo.java b/Database/src/main/java/fredboat/db/repositories/impl/rest/RestGuildPermsRepo.java new file mode 100644 index 000000000..43aedc3a5 --- /dev/null +++ b/Database/src/main/java/fredboat/db/repositories/impl/rest/RestGuildPermsRepo.java @@ -0,0 +1,50 @@ +/* + * + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fredboat.db.repositories.impl.rest; + +import com.google.gson.Gson; +import fredboat.db.entity.main.GuildPermissions; +import fredboat.db.repositories.api.GuildPermsRepo; +import fredboat.util.rest.Http; +import io.prometheus.client.guava.cache.CacheMetricsCollector; + +/** + * Created by napster on 17.02.18. + */ +public class RestGuildPermsRepo extends CachedRestRepo implements GuildPermsRepo { + + public static final String PATH = "guildperms/"; + + public RestGuildPermsRepo(String apiBasePath, Http http, Gson gson, String auth) { + super(apiBasePath + VERSION_PATH + PATH, GuildPermissions.class, http, gson, auth); + } + + @Override + public RestGuildPermsRepo registerCacheStats(CacheMetricsCollector cacheMetrics, String name) { + super.registerCacheStats(cacheMetrics, name); + return this; + } +} diff --git a/Database/src/main/java/fredboat/db/repositories/impl/rest/RestPrefixRepo.java b/Database/src/main/java/fredboat/db/repositories/impl/rest/RestPrefixRepo.java new file mode 100644 index 000000000..d4ab05f2a --- /dev/null +++ b/Database/src/main/java/fredboat/db/repositories/impl/rest/RestPrefixRepo.java @@ -0,0 +1,68 @@ +/* + * + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fredboat.db.repositories.impl.rest; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import fredboat.db.entity.main.Prefix; +import fredboat.db.repositories.api.PrefixRepo; +import fredboat.util.rest.Http; +import io.prometheus.client.guava.cache.CacheMetricsCollector; +import space.npstr.sqlsauce.entities.GuildBotComposite; + +import javax.annotation.Nullable; +import java.io.IOException; + +/** + * Created by napster on 17.02.18. + */ +public class RestPrefixRepo extends CachedRestRepo implements PrefixRepo { + + public static final String PATH = "prefix/"; + + public RestPrefixRepo(String apiBasePath, Http http, Gson gson, String auth) { + super(apiBasePath + VERSION_PATH + PATH, Prefix.class, http, gson, auth); + } + + @Override + public RestPrefixRepo registerCacheStats(CacheMetricsCollector cacheMetrics, String name) { + super.registerCacheStats(cacheMetrics, name); + return this; + } + + @Nullable + @Override + public String getPrefix(GuildBotComposite id) { + try { + String payload = gson.toJson(id); + log.debug("Payload: " + payload); + Http.SimpleRequest getRaw = http.post(path + "getraw", payload, "application/json"); + return gson.fromJson(auth(getRaw).asString(), String.class); + } catch (IOException | JsonSyntaxException e) { + throw new BackendException("Could not get prefix for guild " + id.getGuildId(), e); + } + } +} diff --git a/Database/src/main/java/fredboat/db/repositories/impl/rest/RestRepo.java b/Database/src/main/java/fredboat/db/repositories/impl/rest/RestRepo.java new file mode 100644 index 000000000..977f26d87 --- /dev/null +++ b/Database/src/main/java/fredboat/db/repositories/impl/rest/RestRepo.java @@ -0,0 +1,110 @@ +/* + * + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fredboat.db.repositories.impl.rest; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import fredboat.db.repositories.api.Repo; +import fredboat.util.rest.Http; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import space.npstr.sqlsauce.entities.SaucedEntity; + +import java.io.IOException; +import java.io.Serializable; + +/** + * Created by napster on 17.02.18. + * + * Counterpart to the EntityController of the Quarterdeck module. + */ +public abstract class RestRepo> implements Repo { + + protected static final Logger log = LoggerFactory.getLogger(RestRepo.class); + + public static final int API_VERSION = 0; + public static final String VERSION_PATH = "v" + API_VERSION + "/"; + + protected final String path; + protected final Class entityClass; + protected final Http http; + protected final Gson gson; + protected final String auth; + + /** + * @param path base path of this resource, including the version and a trailing slash + * Example: http://quarterdeck:4269/v1/blacklist/ + */ + public RestRepo(String path, Class entityClass, Http http, Gson gson, String auth) { + this.path = path; + this.entityClass = entityClass; + this.http = http; + this.gson = gson; + this.auth = auth; + } + + public Class getEntityClass() { + return entityClass; + } + + @Override + public void delete(I id) { //todo success handling? + try { + Http.SimpleRequest delete = http.post(path + "delete", gson.toJson(id), "application/json"); + //noinspection ResultOfMethodCallIgnored + auth(delete).execute(); + } catch (IOException | JsonSyntaxException e) { + throw new BackendException(String.format("Could not delete entity with id %s of class %s", id, entityClass), e); + } + } + + @Override + public E fetch(I id) { + try { + Http.SimpleRequest fetch = http.post(path + "fetch", gson.toJson(id), "application/json"); + return gson.fromJson(auth(fetch).asString(), entityClass); + } catch (IOException | JsonSyntaxException e) { + throw new BackendException(String.format("Could not fetch entity with id %s of class %s", id, entityClass), e); + } + } + + @Override + public E merge(E entity) { + try { + Http.SimpleRequest merge = http.post(path + "merge", gson.toJson(entity), "application/json"); + return gson.fromJson(auth(merge).asString(), entityClass); + } catch (IOException | JsonSyntaxException e) { + throw new BackendException(String.format("Could not merge entity with id %s of class %s", entity.getId(), entityClass), e); + } + } + + /** + * @return the provided request but authed + */ + protected Http.SimpleRequest auth(Http.SimpleRequest request) { + return request.auth(auth); + } +} diff --git a/Database/src/main/java/fredboat/db/repositories/impl/rest/RestSearchResultRepo.java b/Database/src/main/java/fredboat/db/repositories/impl/rest/RestSearchResultRepo.java new file mode 100644 index 000000000..fd06c4953 --- /dev/null +++ b/Database/src/main/java/fredboat/db/repositories/impl/rest/RestSearchResultRepo.java @@ -0,0 +1,67 @@ +/* + * + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fredboat.db.repositories.impl.rest; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import fredboat.db.entity.cache.SearchResult; +import fredboat.db.repositories.api.SearchResultRepo; +import fredboat.util.rest.Http; +import io.prometheus.client.guava.cache.CacheMetricsCollector; + +import javax.annotation.Nullable; +import java.io.IOException; + +/** + * Created by napster on 17.02.18. + */ +public class RestSearchResultRepo extends CachedRestRepo implements SearchResultRepo { + + public static final String PATH = "searchresult/"; + + public RestSearchResultRepo(String apiBasePath, Http http, Gson gson, String auth) { + super(apiBasePath + VERSION_PATH + PATH, SearchResult.class, http, gson, auth); + } + + @Override + public RestSearchResultRepo registerCacheStats(CacheMetricsCollector cacheMetrics, String name) { + super.registerCacheStats(cacheMetrics, name); + return this; + } + + @Nullable + @Override + public SearchResult getMaxAged(SearchResult.SearchResultId id, long maxAgeMillis) { + try { + String url = path + "getmaxaged"; + Http.SimpleRequest getMaxAged = http.post(url, gson.toJson(id), "application/json") + .url(url, Http.Params.of("millis", Long.toString(maxAgeMillis))); + return gson.fromJson(auth(getMaxAged).asString(), SearchResult.class); + } catch (IOException | JsonSyntaxException e) { + throw new BackendException("Could not get search result for " + id, e); + } + } +} diff --git a/Database/src/main/java/fredboat/db/repositories/impl/rest/package-info.java b/Database/src/main/java/fredboat/db/repositories/impl/rest/package-info.java new file mode 100644 index 000000000..1bddf5981 --- /dev/null +++ b/Database/src/main/java/fredboat/db/repositories/impl/rest/package-info.java @@ -0,0 +1,29 @@ +/* + * + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +@space.npstr.annotations.FieldsAreNonNullByDefault +@space.npstr.annotations.ParametersAreNonnullByDefault +@space.npstr.annotations.ReturnTypesAreNonNullByDefault +package fredboat.db.repositories.impl.rest; diff --git a/Database/src/main/resources/ehcache_cache.xml b/Database/src/main/resources/ehcache_cache.xml index 8620366f0..635983ef0 100644 --- a/Database/src/main/resources/ehcache_cache.xml +++ b/Database/src/main/resources/ehcache_cache.xml @@ -38,26 +38,26 @@ diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" statistics="true"> - + - + - + - + diff --git a/Database/src/main/resources/ehcache_main.xml b/Database/src/main/resources/ehcache_main.xml index 7bb142074..ad0a69d5b 100644 --- a/Database/src/main/resources/ehcache_main.xml +++ b/Database/src/main/resources/ehcache_main.xml @@ -38,7 +38,7 @@ diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" statistics="true"> - + - + - + - + - + - + - + diff --git a/FredBoat/.dockerignore b/FredBoat/.dockerignore index fc46b7361..0ca32a143 100644 --- a/FredBoat/.dockerignore +++ b/FredBoat/.dockerignore @@ -2,5 +2,4 @@ #ignore everything except the following files, they are used in the Dockerfile to build the fredboat image !Dockerfile !FredBoat.jar -!config.yaml -!credentials.yaml.example +!fredboat.example.yaml diff --git a/FredBoat/Dockerfile b/FredBoat/Dockerfile index 9c62a0737..bab7a7e1d 100644 --- a/FredBoat/Dockerfile +++ b/FredBoat/Dockerfile @@ -1,11 +1,10 @@ -FROM openjdk:8-jdk-slim +FROM openjdk:9-jdk-slim ENV ENV docker RUN mkdir -p /opt/FredBoat -COPY config.yaml /opt/FredBoat/config.yaml -COPY credentials.yaml.example /opt/FredBoat/credentials.yaml +COPY fredboat.example.yaml /opt/FredBoat/fredboat.yaml COPY FredBoat.jar /opt/FredBoat/FredBoat.jar EXPOSE 1356 diff --git a/FredBoat/build.gradle b/FredBoat/build.gradle index 4a59fe627..d904e7c14 100644 --- a/FredBoat/build.gradle +++ b/FredBoat/build.gradle @@ -1,5 +1,4 @@ apply plugin: 'com.gorylenko.gradle-git-properties' -apply plugin: 'com.sedmelluq.jdaction' apply plugin: 'application' apply plugin: 'propdeps' apply plugin: 'propdeps-idea' @@ -55,7 +54,6 @@ dependencies { compile group: 'it.unimi.dsi', name: 'fastutil', version: fastUtilVersion compile group: 'org.togglz', name: 'togglz-core', version: togglzVersion - compile group: 'com.google.guava', name: 'guava', version: guavaVersion compile group: 'com.github.vladimir-bukhtoyarov', name: 'bucket4j-core', version: bucket4jVersion //tests diff --git a/FredBoat/config.yaml b/FredBoat/config.yaml deleted file mode 100644 index 0482a46f7..000000000 --- a/FredBoat/config.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -development: true # Set this to false for selfhosting. If you leave this enabled and complain about weird - # things happening to your bot in the selfhosting chat you will be publicly taunted. - -prefix: '<<' # Default prefix used by the bot -restServerEnabled: true # Set this to false if you are running multiple FredBoat bots on the same machine -admins: [] # Add comma separated userIds and roleIds that should have access to bot admin commands. Find role ids with the ;;roleinfo command -useAutoBlacklist: true # Set to true to automatically blacklist users who frequently hit the rate limits -game: "" # Set the displayed game/status. Leave empty quote marks for the default status -continuePlayback: false # Set to true to force the player to continue playback even if left alone - -enableYouTube: true # Set to true to enable playing YouTube links -enableSoundCloud: true # Set to true to enable playing SoundCloud links -enableBandCamp: true # Set to true to enable playing BandCamp links -enableTwitch: true # Set to true to enable playing Twitch links -enableVimeo: true # Set to true to enable playing Vimeo links -enableMixer: true # Set to true to enable playing Mixer links -enableSpotify: true # Set to true to enable playing Spotify links -enableLocal: false # Set to true to enable playing local files -enableHttp: false # Set to true to enable playing direct links diff --git a/FredBoat/credentials.yaml.example b/FredBoat/credentials.yaml.example deleted file mode 100644 index d5d7a6670..000000000 --- a/FredBoat/credentials.yaml.example +++ /dev/null @@ -1,163 +0,0 @@ ---- - -################################################################ -### *** WARNING *** -################################################################ -### -### ALMOST EVERYTHING REQUESTED IN THIS FILE ARE CONFIDENTIAL CREDENTIALS -### IF YOU POST THIS FILE ONLINE (such as on GitHub) YOUR BOT COULD BE COMPROMISED -### -### -### Use a proper text editor when editing this file, for example Sublime. -### Do not use tab characters in this file, use plain spaces. -### -### Keep at least one space after a colon, like so: -### -### key: value -### -### You can wrap most values into quotation marks, except numbers and booleans: -### -### someUrl: "http://example.com" -### someToken: "123.qwe.456[DFG=" -### somePortNumber: 22 -### useSomeFeature: true -### -### More information on correctly formatting yaml files: http://www.yaml.org/start.html - - - -################################################################ -### Essential credentials -################################################################ - -# Add your discord bot token below, between the quotation marks -# Find the token of your bot on https://discordapp.com/developers/applications/me -# Tutorial: https://github.com/reactiflux/discord-irc/wiki/Creating-a-discord-bot-&-getting-a-token -token: "PutYourDiscordBotTokenHere" - - -# Used by the ;;split and ;;np commands. Must be hooked up to the Youtube Data API. -# Add your google API key between the quotation marks -# How to get the key: https://developers.google.com/youtube/registering_an_application -googleServerKeys: "PutYourGoogleAPIKeyHere" - - - -################################################################ -### Optional APIs -################################################################ - -# For the ;;mal command -# Create an account on https://myanimelist.net/ and enter its login below -malUser: "" -malPassword: "" - - -# Used to access imgur galleries for some RandomImageCommands -# Acquired from here: https://api.imgur.com/oauth2/addclient -# Choose an option that does not require an Authorization callback URL -imgurClientId: "" - - -# Used to retrieve Spotify playlists -# Get them from here: https://developer.spotify.com/my-applications -spotifyId: "" -spotifySecret: "" - - -# Used by ;;weather command. -# Get them from: http://openweathermap.org/appid -openWeatherKey: "" - - - -################################################################ -### Developers and very experienced users only -################################################################ - - -# FredBoat was written to work with PostgreSQL. -# If you are running with docker-compose then you don't need to change the jdbcUrl here. -# In PostgreSQL, role means user and vice versa. Keep that in mind when reading the following help and the provided links. -# If you are running your own PostgreSQL database, you will need to provide a role and a database belonging to that role. -# The role needs at least the permission to log in. -# All postgres databases used by FredBoat are required to have the Hstore extension enabled. -# Learn more about roles here: https://www.postgresql.org/docs/10/static/database-roles.html -# Learn more about creating databases here: https://www.postgresql.org/docs/10/static/manage-ag-createdb.html -# Learn more about the postgres jdbc url here: https://jdbc.postgresql.org/documentation/head/connect.html -# Learn more about creating extensions here: https://www.postgresql.org/docs/current/static/sql-createextension.html -# If you are using an SSH tunnel, you need to point your jdbc url to localhost and the configured tunnelLocalPort -# Example jdbc: "jdbc:postgresql://localhost:5432/fredboat?user=fredboat&password=youshallnotpass" -jdbcUrl: "" - - -# Ssh tunnel for a remote database. this is useful for when you don't want to expose your database on the remote server -# and instead use ssh tunneling to access it -# If you are running with docker-compose then you don't need to change any ssh value here. -# If you are using an SSH tunnel, you need to point your jdbc url to localhost and the configured tunnelLocalPort -useSshTunnel: false -sshHost: "" # add the ssh port to the ip / url, usually 22, for example: "db.example.com:22" -sshUser: "" # user on the remote machine -sshPrivateKeyFile: "" # path to an ssh private key file which is authorized to log in to the sshUser on the remote machine. learn how to create these: https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-16-04#step-four-—-add-public-key-authentication-(recommended) -sshKeyPassphrase: "" # optional passphrase for the ssh key file -tunnelLocalPort: 5432 # endpoint port of the tunnel on the machine running fredboat; makes sure these dont collide; this one needs to be used in the jdbc url -tunnelRemotePort: 5432 # port of the PostgreSQL on the remote machine, 5432 by default - -# Database for caching things, see config of main database above for details about the individual values. -# If you are running with docker-compose then you don't need to change the cache jdbcUrl here. -# The main and cache databases can be two databases inside a single postgres instance. -# They CANNOT be the same database due to the way flyway migrations work. -# The main benefit is that you don't have to backup/migrate the cache database, it can just be dropped/recreated -# If you do not provide a jdbc url for the cache database, FredBoat will still work (most likely), but may have a degraded -# performance, especially in high usage environments and when using Spotify playlists. -# If you are going to use two different ssh tunnels for both database connections, make sure that the local tunnel ports don't collide -cacheJdbcUrl: "" - -cacheUseSshTunnel: false -cacheSshHost: "" -cacheSshUser: "" -cacheSshPrivateKeyFile: "" -cacheSshKeyPassphrase: "" -cacheTunnelLocalPort: 5433 -cacheTunnelRemotePort: 5432 - - - -# Additional google keys -# If you find yourself running a big bot and hitting youtube API ratelimits, you can add several keys via the list below. -# Remember to comment out the other "googleServerKeys" entry in this file, you can have only one of them. -# FredBoat will pick the keys by random to to access Youtube. -#googleServerKeys: ["Key1", "Key2", "Key3"] - - -# Error aggregation service https://sentry.io/ -sentryDsn: "" - - -# If you are running lavalink nodes this is the place to add them. -# Examples shown below, don't forget to uncomment them properly. -# More on Lavalink: https://github.com/Frederikam/Lavalink -#lavalinkHosts: - #- name : "local" - # host : "ws://localhost:5555" - # pass : "youshallnotpass" - #- name : "remote1" - # host : "ws://192.168.1.20:5556" - # pass : "youshallnotpass" - #- name : "remote2" - # host : "ws://lavalink.example.com:5557" - # pass : "youshallnotpass" - - -# Webhooks to Discord channels that will post some guild stats and shard status changes -# More information on webhooks: https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks -# Example: "https://canary.discordapp.com/api/webhooks/1234567890/QWERTZUIOPasdfghjklYXCVBNM" (no, this one will not work) -eventLogWebhook: "" # webhook url for connect / disconnect events -eventLogInterval: 1 # interval at which connect / disconnect events are posted in minutes -guildStatsWebhook: "" # webhook url for guild stats -guildStatsInterval: 60 # interval at which guild stats are posted in minutes - - -# Post build tool test results into a discord channel. The token must be a discord bot token. -testToken: "" -testChannelId: "" diff --git a/FredBoat/fredboat.example.yaml b/FredBoat/fredboat.example.yaml new file mode 100644 index 000000000..9f727cc9a --- /dev/null +++ b/FredBoat/fredboat.example.yaml @@ -0,0 +1,168 @@ +--- +################################################################ +### *** WARNING *** +################################################################ +### +### ALMOST EVERYTHING REQUESTED IN THIS FILE ARE CONFIDENTIAL CREDENTIALS +### IF YOU POST THIS FILE ONLINE (such as on GitHub) YOUR BOT COULD BE COMPROMISED +### +### +### Use a proper text editor when editing this file, for example Sublime. +### Do not use tab characters in this file, use plain spaces. +### +### Keep at least one space after a colon, like so: +### +### key: value +### +### +### Never edit or add a value to lines that have no default value, like: +### +### credentials: +### +### Just leave them be. A default value may be an empty string like so: "" +### +### +### You can wrap most values into quotation marks, except numbers and booleans: +### +### someUrl: "http://example.com" +### someToken: "123.qwe.456[DFG=" +### somePortNumber: 22 +### useSomeFeature: true +### +### +### You can have a list of things like this: +### +### listOfStrings: ["string1", "string2", "string3"] +### +### or like this: +### +### listOfStrings: +### - "string1" +### - "string2" +### - "string3" +### +### +### More information on correctly formatting yaml files: http://www.yaml.org/start.html + + +################################################################ +### Basic configuration +################################################################ + +config: + development: true # Set this to false for selfhosting. If you leave this enabled and complain about weird + # things happening to your bot in the selfhosting chat you will be publicly taunted. + prefix: '<<' # Default prefix used by the bot + restServerEnabled: false # Set this to true to enable FredBoats legacy API. Might cause issues when running several bots together + botAdmins: [] # Add comma separated userIds and roleIds that should have access to bot admin commands. Find role ids with the ;;roleinfo command + autoBlacklist: true # Set to true to automatically blacklist users who frequently hit the rate limits + game: "" # Set the displayed game/status. Leave empty quote marks for the default status + continuePlayback: false # Set to true to force the player to continue playback even if left alone + +server: + port: 1356 # Change the port of the API FredBoat exposes +spring: + main: + web-application-type: reactive # Do not touch for now. This will enable / disable a possible future FredBoat API + + +audio-sources: + enableYouTube: true # Set to true to enable playing YouTube links + enableSoundCloud: true # Set to true to enable playing SoundCloud links + enableBandCamp: true # Set to true to enable playing BandCamp links + enableTwitch: true # Set to true to enable playing Twitch links + enableVimeo: true # Set to true to enable playing Vimeo links + enableMixer: true # Set to true to enable playing Mixer links + enableSpotify: true # Set to true to enable playing Spotify links + enableLocal: false # Set to true to enable playing local files + enableHttp: false # Set to true to enable playing direct links + + +################################################################ +### Essential credentials +################################################################ + +backend: + quarterdeck: + # Host address of your quarterdeck backend, including port unless you are using a reverse proxy. + # Example: https://such.example.com:4269/ + # No need set the host when running the whole FredBoat in docker. + host: "" + # Admin username and pass that you configured in the quarterdeck.yaml. + # Do not leave any of them empty. + user: "" + pass: "" + + +credentials: + # Add your discord bot token below, between the quotation marks + # Find the token of your bot on https://discordapp.com/developers/applications/me + # Tutorial: https://github.com/reactiflux/discord-irc/wiki/Creating-a-discord-bot-&-getting-a-token + discordBotToken: "PutYourDiscordBotTokenHere" + + + # Used by the ;;split and ;;np commands. Must be hooked up to the Youtube Data API. + # You can add additional keys in case you are running a big bot + # How to get the key: https://developers.google.com/youtube/registering_an_application + # Add your google API key between the quotation marks + googleApiKeys: + - "PutYourGoogleAPIKeyHere" + + + +################################################################ +### Optional APIs +################################################################ + + # For the ;;mal command + # Create an account on https://myanimelist.net/ and enter its login below + malUser: "" + malPassword: "" + + + # Used to access imgur galleries for some RandomImageCommands + # Acquired from here: https://api.imgur.com/oauth2/addclient + # Choose an option that does not require an Authorization callback URL + imgurClientId: "" + + + # Used to retrieve Spotify playlists + # Get them from here: https://developer.spotify.com/my-applications + spotifyId: "" + spotifySecret: "" + + + # Used by ;;weather command. + # Get them from: http://openweathermap.org/appid + openWeatherKey: "" + + # Error aggregation service https://sentry.io/ + sentryDsn: "" + + + +event-logger: + # Webhooks to Discord channels that will post some guild stats and shard status changes + # More information on webhooks: https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks + # Example: "https://canary.discordapp.com/api/webhooks/1234567890/QWERTZUIOPasdfghjklYXCVBNM" (no, this one will not work) + eventLogWebhook: "" # webhook url for connect / disconnect events + eventLogInterval: 1 # interval at which connect / disconnect events are posted in minutes + guildStatsWebhook: "" # webhook url for guild stats + guildStatsInterval: 60 # interval at which guild stats are posted in minutes + + + +# If you are running lavalink nodes this is the place to add them. +# Examples shown below, don't forget to uncomment them properly. +# More on Lavalink: https://github.com/Frederikam/Lavalink +#lavalink: +# nodes: +# - name : "local" +# host : "ws://localhost:5555" +# pass : "youshallnotpass" +# - name : "remote1" +# host : "ws://192.168.1.20:5556" +# pass : "youshallnotpass" +# - name : "remote2" +# host : "ws://lavalink.example.com:5557" +# pass : "youshallnotpass" diff --git a/FredBoat/src/main/java/fredboat/api/API.java b/FredBoat/src/main/java/fredboat/api/API.java index f5adb4f07..383b2cffa 100644 --- a/FredBoat/src/main/java/fredboat/api/API.java +++ b/FredBoat/src/main/java/fredboat/api/API.java @@ -41,21 +41,21 @@ public class API { - private static final Logger log = LoggerFactory.getLogger(API.class); + public static final int DEFAULT_PORT = 1356; - private static final int PORT = 1356; + private static final Logger log = LoggerFactory.getLogger(API.class); private API() {} - public static void start(PlayerRegistry playerRegistry, BotMetrics botMetrics, ShardProvider shardProvider) { + public static void start(PlayerRegistry playerRegistry, BotMetrics botMetrics, ShardProvider shardProvider, + int apiPort) { if (!Launcher.getBotController().getAppConfig().isRestServerEnabled()) { log.warn("Rest server is not enabled. Skipping Spark ignition!"); return; } - log.info("Igniting Spark API on port: " + PORT); - - Spark.port(PORT); + log.info("Igniting Spark API on port: " + apiPort); + Spark.port(apiPort); Spark.before((request, response) -> { log.info(request.requestMethod() + " " + request.pathInfo()); @@ -105,7 +105,7 @@ public static void start(PlayerRegistry playerRegistry, BotMetrics botMetrics, S public static void turnOnMetrics(MetricsServletAdapter metricsServlet) { if (!Launcher.getBotController().getAppConfig().isRestServerEnabled()) { - log.warn("Rest server is not enabled. Skipping Spark ignition!"); + log.warn("Rest server is not enabled. Metrics will not be scrapable!"); return; } diff --git a/FredBoat/src/main/java/fredboat/audio/player/AudioConnectionFacade.java b/FredBoat/src/main/java/fredboat/audio/player/AudioConnectionFacade.java index 3cd6685d8..f8b6560eb 100644 --- a/FredBoat/src/main/java/fredboat/audio/player/AudioConnectionFacade.java +++ b/FredBoat/src/main/java/fredboat/audio/player/AudioConnectionFacade.java @@ -63,7 +63,7 @@ public AudioConnectionFacade(LavalinkConfig lavalinkConfig, Credentials credenti ShardProvider shardProvider) { this.debugConnectionListenerProvider = debugConnectionListenerProvider; this.audioPlayerManager = audioPlayerManager; - if (lavalinkConfig.getLavalinkHosts().isEmpty()) { + if (lavalinkConfig.getNodes().isEmpty()) { lavalink = null; //local playback audioPlayerManager.enableGcMonitoring(); return; @@ -76,9 +76,9 @@ public AudioConnectionFacade(LavalinkConfig lavalinkConfig, Credentials credenti ); Runtime.getRuntime().addShutdownHook(new Thread(lavalink::shutdown, "lavalink-shutdown-hook")); - List hosts = lavalinkConfig.getLavalinkHosts(); - hosts.forEach(lavalinkHost -> lavalink.addNode(lavalinkHost.getName(), lavalinkHost.getUri(), - lavalinkHost.getPassword())); + List nodes = lavalinkConfig.getNodes(); + nodes.forEach(lavalinkNode -> lavalink.addNode(lavalinkNode.getName(), lavalinkNode.getUri(), + lavalinkNode.getPassword())); new LavalinkCollector(lavalink).register(); } diff --git a/FredBoat/src/main/java/fredboat/audio/player/GuildPlayer.java b/FredBoat/src/main/java/fredboat/audio/player/GuildPlayer.java index ce401414f..2d027c2ff 100644 --- a/FredBoat/src/main/java/fredboat/audio/player/GuildPlayer.java +++ b/FredBoat/src/main/java/fredboat/audio/player/GuildPlayer.java @@ -32,7 +32,7 @@ import fredboat.command.music.control.VoteSkipCommand; import fredboat.commandmeta.MessagingException; import fredboat.commandmeta.abs.CommandContext; -import fredboat.db.EntityIO; +import fredboat.db.api.GuildConfigService; import fredboat.definitions.PermissionLevel; import fredboat.feature.I18n; import fredboat.jda.JdaEntityProvider; @@ -69,19 +69,19 @@ public class GuildPlayer extends AbstractPlayer { private final MusicTextChannelProvider musicTextChannelProvider; private final JdaEntityProvider jdaEntityProvider; private final AudioConnectionFacade audioConnectionFacade; - private final EntityIO entityIO; + private final GuildConfigService guildConfigService; @SuppressWarnings("LeakingThisInConstructor") public GuildPlayer(Guild guild, MusicTextChannelProvider musicTextChannelProvider, JdaEntityProvider jdaEntityProvider, - AudioConnectionFacade audioConnectionFacade, AudioPlayerManager audioPlayerManager, EntityIO entityIO, - Ratelimiter ratelimiter) { + AudioConnectionFacade audioConnectionFacade, AudioPlayerManager audioPlayerManager, + GuildConfigService guildConfigService, Ratelimiter ratelimiter) { super(guild.getId(), audioConnectionFacade); log.debug("Constructing GuildPlayer({})", guild.getIdLong()); this.jdaEntityProvider = jdaEntityProvider; this.musicTextChannelProvider = musicTextChannelProvider; this.audioConnectionFacade = audioConnectionFacade; - this.entityIO = entityIO; + this.guildConfigService = guildConfigService; onPlayHook = this::announceTrack; onErrorHook = this::handleError; @@ -423,7 +423,7 @@ private boolean isTrackAnnounceEnabled() { try { Guild guild = getGuild(); if (guild != null) { - enabled = entityIO.fetchGuildConfig(guild).isTrackAnnounce(); + enabled = guildConfigService.fetchGuildConfig(guild).isTrackAnnounce(); } } catch (Exception ignored) { } diff --git a/FredBoat/src/main/java/fredboat/audio/player/PlayerRegistry.java b/FredBoat/src/main/java/fredboat/audio/player/PlayerRegistry.java index de07eb3b9..117ec9058 100644 --- a/FredBoat/src/main/java/fredboat/audio/player/PlayerRegistry.java +++ b/FredBoat/src/main/java/fredboat/audio/player/PlayerRegistry.java @@ -26,7 +26,7 @@ package fredboat.audio.player; import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager; -import fredboat.db.EntityIO; +import fredboat.db.api.GuildConfigService; import fredboat.jda.JdaEntityProvider; import fredboat.util.ratelimit.Ratelimiter; import net.dv8tion.jda.core.entities.Guild; @@ -48,19 +48,19 @@ public class PlayerRegistry { private final Map registry = new ConcurrentHashMap<>(); private final JdaEntityProvider jdaEntityProvider; private final AudioConnectionFacade audioConnectionFacade; - private final EntityIO entityIO; + private final GuildConfigService guildConfigService; private final AudioPlayerManager audioPlayerManager; private final Ratelimiter ratelimiter; private final MusicTextChannelProvider musicTextChannelProvider; public PlayerRegistry(MusicTextChannelProvider musicTextChannelProvider, JdaEntityProvider jdaEntityProvider, - AudioConnectionFacade audioConnectionFacade, EntityIO entityIO, + AudioConnectionFacade audioConnectionFacade, GuildConfigService guildConfigService, @Qualifier("loadAudioPlayerManager") AudioPlayerManager audioPlayerManager, Ratelimiter ratelimiter) { this.musicTextChannelProvider = musicTextChannelProvider; this.jdaEntityProvider = jdaEntityProvider; this.audioConnectionFacade = audioConnectionFacade; - this.entityIO = entityIO; + this.guildConfigService = guildConfigService; this.audioPlayerManager = audioPlayerManager; this.ratelimiter = ratelimiter; } @@ -70,7 +70,7 @@ public GuildPlayer getOrCreate(@Nonnull Guild guild) { return registry.computeIfAbsent( guild.getIdLong(), guildId -> { GuildPlayer p = new GuildPlayer(guild, musicTextChannelProvider, jdaEntityProvider, - audioConnectionFacade, audioPlayerManager, entityIO, ratelimiter); + audioConnectionFacade, audioPlayerManager, guildConfigService, ratelimiter); p.setVolume(DEFAULT_VOLUME); return p; }); diff --git a/FredBoat/src/main/java/fredboat/command/admin/TestCommand.java b/FredBoat/src/main/java/fredboat/command/admin/TestCommand.java deleted file mode 100644 index 59343ae18..000000000 --- a/FredBoat/src/main/java/fredboat/command/admin/TestCommand.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2017 Frederik Ar. Mikkelsen - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package fredboat.command.admin; - -import fredboat.commandmeta.abs.Command; -import fredboat.commandmeta.abs.CommandContext; -import fredboat.commandmeta.abs.ICommandRestricted; -import fredboat.db.DatabaseNotReadyException; -import fredboat.definitions.PermissionLevel; -import fredboat.main.Launcher; -import fredboat.messaging.internal.Context; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import space.npstr.sqlsauce.DatabaseConnection; -import space.npstr.sqlsauce.DatabaseException; - -import javax.annotation.Nonnull; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceException; - -/** - * Stress tests the database - */ -public class TestCommand extends Command implements ICommandRestricted { - - private static final Logger log = LoggerFactory.getLogger(TestCommand.class); - - private enum Result {WORKING, SUCCESS, FAILED} - - private final String DROP_TEST_TABLE = "DROP TABLE IF EXISTS test;"; - private final String CREATE_TEST_TABLE = "CREATE TABLE IF NOT EXISTS test (id serial, val integer, PRIMARY KEY (id));"; - private final String INSERT_TEST_TABLE = "INSERT INTO test (val) VALUES (:val) "; - - public TestCommand(String name, String... aliases) { - super(name, aliases); - } - - @Override - public void onInvoke(@Nonnull CommandContext context) { - Launcher.getBotController().getExecutor().submit( - () -> invoke(Launcher.getBotController().getDatabaseManager().getMainDbConn(), context, context.args) - ); - } - - boolean invoke(DatabaseConnection dbConn, Context context, String args[]) { - - boolean result = false; - - int t = 20; - int o = 2000; - if (args.length > 1) { - t = Integer.valueOf(args[0]); - o = Integer.valueOf(args[1]); - } - final int threads = t; - final int operations = o; - if (context.getTextChannel() != null && context.getMember() != null) { - context.replyWithName("Beginning stress test with " + threads + " threads each doing " + operations + " operations"); - } - - prepareStressTest(dbConn); - long started = System.currentTimeMillis(); - Result[] results = new Result[threads]; - Throwable[] exceptions = new Throwable[threads]; - - for (int i = 0; i < threads; i++) { - results[i] = Result.WORKING; - new StressTestThread(i, operations, results, exceptions, dbConn).start(); - } - - //wait for when it's done and report the results - int maxTime = 600000; //give it max 10 mins to run - int sleep = 10; //ms - int maxChecks = maxTime / sleep; - int c = 0; - while (!doneYet(results) || c >= maxChecks) { - c++; - try { - Thread.sleep(sleep); - } catch (InterruptedException e) { - //duh - } - } - - String out = "`DB stress test results:"; - for (int i = 0; i < results.length; i++) { - out += "\nThread #" + i + ": "; - if (results[i] == Result.WORKING) { - out += "failed to get it done in " + maxTime / 1000 + " seconds"; - result = false; - } else if (results[i] == Result.FAILED) { - exceptions[i].printStackTrace(); - out += "failed with an exception: " + exceptions[i].toString(); - result = false; - } else if (results[i] == Result.SUCCESS) { - out += "successful"; - result = true; - } - } - out += "\n Time taken: " + ((System.currentTimeMillis() - started)) + "ms for " + (threads * operations) + " requested operations.`"; - log.info(out); - if (context.getTextChannel() != null && context.getMember() != null) { - context.replyWithName(out); - } - - return result; - } - - private boolean doneYet(Result[] results) { - for (int i = 0; i < results.length; i++) { - if (results[i] == Result.WORKING) { - return false; - } - } - return true; - } - - private void prepareStressTest(DatabaseConnection dbConn) { - //drop and recreate the test table - EntityManager em = null; - try { - em = dbConn.getEntityManager(); - em.getTransaction().begin(); - em.createNativeQuery(DROP_TEST_TABLE).executeUpdate(); - em.createNativeQuery(CREATE_TEST_TABLE).executeUpdate(); - em.getTransaction().commit(); - } catch (DatabaseException | PersistenceException e) { - throw new DatabaseNotReadyException(e); - } finally { - if (em != null) { - em.close(); - } - } - } - - private class StressTestThread extends Thread { - - private int number; - private int operations; - private Result[] results; - private Throwable[] exceptions; - private DatabaseConnection dbConn; - - - StressTestThread(int number, int operations, Result[] results, Throwable[] exceptions, DatabaseConnection dbConn) { - super(StressTestThread.class.getSimpleName() + " number"); - this.number = number; - this.operations = operations; - this.results = results; - this.exceptions = exceptions; - this.dbConn = dbConn; - } - - @Override - public void run() { - boolean failed = false; - EntityManager em = null; - try { - for (int i = 0; i < operations; i++) { - em = dbConn.getEntityManager(); - try { - em.getTransaction().begin(); - em.createNativeQuery(INSERT_TEST_TABLE) - .setParameter("val", (int) (Math.random() * 10000)) - .executeUpdate(); - em.getTransaction().commit(); - } finally { - em.close(); //go crazy and request and close the EM for every single operation, this is a stress test after all - } - } - } catch (Exception e) { - results[number] = Result.FAILED; - exceptions[number] = e; - failed = true; - if (em != null) - em.close(); - } - - if (!failed) - results[number] = Result.SUCCESS; - } - } - - @Nonnull - @Override - public String help(@Nonnull Context context) { - return "{0}{1} [n m]\n#Stress test the database with n threads each doing m operations. Results will be shown after max 10 minutes."; - } - - @Nonnull - @Override - public PermissionLevel getMinimumPerms() { - return PermissionLevel.BOT_OWNER; - } -} diff --git a/FredBoat/src/main/java/fredboat/command/config/ConfigCommand.java b/FredBoat/src/main/java/fredboat/command/config/ConfigCommand.java index 0aa467483..7507ba9fa 100644 --- a/FredBoat/src/main/java/fredboat/command/config/ConfigCommand.java +++ b/FredBoat/src/main/java/fredboat/command/config/ConfigCommand.java @@ -58,7 +58,7 @@ public void onInvoke(@Nonnull CommandContext context) { } private void printConfig(CommandContext context) { - GuildConfig gc = Launcher.getBotController().getEntityIO().fetchGuildConfig(context.guild); + GuildConfig gc = Launcher.getBotController().getGuildConfigService().fetchGuildConfig(context.guild); MessageBuilder mb = CentralMessaging.getClearThreadLocalMessageBuilder() .append(context.i18nFormat("configNoArgs", context.guild.getName())).append("\n") @@ -86,7 +86,7 @@ private void setConfig(CommandContext context) { switch (key) { case "track_announce": if (val.equalsIgnoreCase("true") | val.equalsIgnoreCase("false")) { - Launcher.getBotController().getEntityIO().transformGuildConfig( + Launcher.getBotController().getGuildConfigService().transformGuildConfig( context.guild, gc -> gc.setTrackAnnounce(Boolean.valueOf(val))); context.replyWithName("`track_announce` " + context.i18nFormat("configSetTo", val)); } else { @@ -95,7 +95,7 @@ private void setConfig(CommandContext context) { break; case "auto_resume": if (val.equalsIgnoreCase("true") | val.equalsIgnoreCase("false")) { - Launcher.getBotController().getEntityIO().transformGuildConfig( + Launcher.getBotController().getGuildConfigService().transformGuildConfig( context.guild, gc -> gc.setAutoResume(Boolean.valueOf(val))); context.replyWithName("`auto_resume` " + context.i18nFormat("configSetTo", val)); } else { diff --git a/FredBoat/src/main/java/fredboat/command/config/ModulesCommand.java b/FredBoat/src/main/java/fredboat/command/config/ModulesCommand.java index 9ffca37a8..8d76a681d 100644 --- a/FredBoat/src/main/java/fredboat/command/config/ModulesCommand.java +++ b/FredBoat/src/main/java/fredboat/command/config/ModulesCommand.java @@ -108,12 +108,12 @@ public void onInvoke(@Nonnull CommandContext context) { output = context.i18nFormat("moduleDisable", "**" + context.i18n(module.translationKey) + "**"); } - Launcher.getBotController().getEntityIO().transformGuildModules(context.guild, transform); + Launcher.getBotController().getGuildModulesService().transformGuildModules(context.guild, transform); context.reply(output);//if the transaction right above this line fails, it won't be reached, which is intended } private static void displayModuleStatus(@Nonnull CommandContext context) { - GuildModules gm = Launcher.getBotController().getEntityIO().fetchGuildModules(context.guild); + GuildModules gm = Launcher.getBotController().getGuildModulesService().fetchGuildModules(context.guild); Function moduleStatusFormatter = moduleStatusLine(gm, context); String moduleStatus = ""; diff --git a/FredBoat/src/main/java/fredboat/command/config/PermissionsCommand.java b/FredBoat/src/main/java/fredboat/command/config/PermissionsCommand.java index dbf1a86c1..44d3d29fc 100644 --- a/FredBoat/src/main/java/fredboat/command/config/PermissionsCommand.java +++ b/FredBoat/src/main/java/fredboat/command/config/PermissionsCommand.java @@ -136,7 +136,7 @@ public void remove(CommandContext context) { context.replyWithName(context.i18nFormat("permsRemoved", mentionableToName(selected), permissionLevel)); return gp.setFromEnum(permissionLevel, newList); }; - Launcher.getBotController().getEntityIO().transformGuildPerms(context.guild, transformation); + Launcher.getBotController().getGuildPermsService().transformGuildPerms(context.guild, transformation); } public void add(CommandContext context) { @@ -166,13 +166,13 @@ public void add(CommandContext context) { TextUtils.escapeMarkdown(mentionableToName(selected)), permissionLevel)); return gp.setFromEnum(permissionLevel, newList); }; - Launcher.getBotController().getEntityIO().transformGuildPerms(context.guild, transformation); + Launcher.getBotController().getGuildPermsService().transformGuildPerms(context.guild, transformation); } public void list(CommandContext context) { Guild guild = context.guild; Member invoker = context.invoker; - GuildPermissions gp = Launcher.getBotController().getEntityIO().fetchGuildPermissions(guild); + GuildPermissions gp = Launcher.getBotController().getGuildPermsService().fetchGuildPermissions(guild); List mentionables = idsToMentionables(guild, gp.getFromEnum(permissionLevel)); diff --git a/FredBoat/src/main/java/fredboat/command/config/PrefixCommand.java b/FredBoat/src/main/java/fredboat/command/config/PrefixCommand.java index 6722bf389..41319a5df 100644 --- a/FredBoat/src/main/java/fredboat/command/config/PrefixCommand.java +++ b/FredBoat/src/main/java/fredboat/command/config/PrefixCommand.java @@ -69,7 +69,7 @@ public PrefixCommand(@Nullable CacheMetricsCollector cacheMetrics, @Nonnull Stri .expireAfterAccess(1, TimeUnit.MINUTES) //evict inactive guilds .concurrencyLevel(Launcher.getBotController().getCredentials().getRecommendedShardCount()) //each shard has a thread (main JDA thread) accessing this cache many times .build(CacheLoader.asyncReloading(CacheLoader.from( - guildId -> Launcher.getBotController().getEntityIO().getPrefix(new GuildBotComposite(guildId, DiscordUtil.getBotId(Launcher.getBotController().getCredentials())))), + guildId -> Launcher.getBotController().getPrefixService().getPrefix(new GuildBotComposite(guildId, DiscordUtil.getBotId(Launcher.getBotController().getCredentials())))), Launcher.getBotController().getExecutor())); @Nonnull @@ -110,7 +110,7 @@ public void onInvoke(@Nonnull CommandContext context) { newPrefix = context.rawArgs; } - Launcher.getBotController().getEntityIO().transformPrefix(context.guild, prefixEntity -> prefixEntity.setPrefix(newPrefix)); + Launcher.getBotController().getPrefixService().transformPrefix(context.guild, prefixEntity -> prefixEntity.setPrefix(newPrefix)); //we could do a put instead of invalidate here and probably safe one lookup, but that undermines the database // as being the single source of truth for prefixes diff --git a/FredBoat/src/main/java/fredboat/commandmeta/CommandInitializer.java b/FredBoat/src/main/java/fredboat/commandmeta/CommandInitializer.java index 9995b810e..b294b1187 100644 --- a/FredBoat/src/main/java/fredboat/commandmeta/CommandInitializer.java +++ b/FredBoat/src/main/java/fredboat/commandmeta/CommandInitializer.java @@ -89,7 +89,6 @@ public static void initCommands(@Nullable CacheMetricsCollector cacheMetrics, We adminModule.registerCommand(new ReviveCommand("revive")); adminModule.registerCommand(new SentryDsnCommand(sentryConfiguration, "sentrydsn")); adminModule.registerCommand(new SetAvatarCommand("setavatar")); - adminModule.registerCommand(new TestCommand("test")); adminModule.registerCommand(new UnblacklistCommand("unblacklist", "unlimit")); @@ -175,7 +174,7 @@ public static void initCommands(@Nullable CacheMetricsCollector cacheMetrics, We funModule.registerCommand(new RemoteFileCommand("http://i.imgur.com/93VahIh.png", "anime")); funModule.registerCommand(new RemoteFileCommand("http://i.imgur.com/qz6g1vj.gif", "explosion")); funModule.registerCommand(new RemoteFileCommand("http://i.imgur.com/84nbpQe.png", "internetspeed")); - + /* Text Faces & Unicode 'Art' & ASCII 'Art' and Stuff */ funModule.registerCommand(new TextCommand("¯\\_(ツ)_/¯", "shrug", "shr")); funModule.registerCommand(new TextCommand("ಠ_ಠ", "faceofdisapproval", "fod", "disapproving")); diff --git a/FredBoat/src/main/java/fredboat/commandmeta/abs/CommandContext.java b/FredBoat/src/main/java/fredboat/commandmeta/abs/CommandContext.java index e0232c9f5..978062d41 100644 --- a/FredBoat/src/main/java/fredboat/commandmeta/abs/CommandContext.java +++ b/FredBoat/src/main/java/fredboat/commandmeta/abs/CommandContext.java @@ -113,7 +113,7 @@ public boolean hasArguments() { } public Collection getEnabledModules() { - return Launcher.getBotController().getEntityIO().fetchGuildModules(this.guild).getEnabledModules(); + return Launcher.getBotController().getGuildModulesService().fetchGuildModules(this.guild).getEnabledModules(); } @Override diff --git a/FredBoat/src/main/java/fredboat/config/ConfigConfiguration.java b/FredBoat/src/main/java/fredboat/config/ConfigConfiguration.java deleted file mode 100644 index 5eb82ccfc..000000000 --- a/FredBoat/src/main/java/fredboat/config/ConfigConfiguration.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package fredboat.config; - -import fredboat.config.property.*; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Scope; - -/** - * Created by napster on 23.02.18. - *

- * The name is not a meme, I swear. - *

- * Provide refreshing beans to access property configs - */ -@Configuration -public class ConfigConfiguration { - - @Bean - @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public AppConfig appConfig(PropertyConfigProvider configProvider) { - return configProvider.getAppConfig(); - } - - @Bean - @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public AudioSourcesConfig audioSourcesConfig(PropertyConfigProvider configProvider) { - return configProvider.getAudioSourcesConfig(); - } - - @Bean - @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public Credentials credentials(PropertyConfigProvider configProvider) { - return configProvider.getCredentials(); - } - - @Bean - @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public DatabaseConfig databaseConfig(PropertyConfigProvider configProvider) { - return configProvider.getDatabaseConfig(); - } - - @Bean - @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public EventLoggerConfig eventLoggerConfig(PropertyConfigProvider configProvider) { - return configProvider.getEventLoggerConfig(); - } - - @Bean - @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public LavalinkConfig lavalinkConfig(PropertyConfigProvider configProvider) { - return configProvider.getLavalinkConfig(); - } -} diff --git a/FredBoat/src/main/java/fredboat/config/DatabaseConfiguration.java b/FredBoat/src/main/java/fredboat/config/DatabaseConfiguration.java deleted file mode 100644 index 4893c46b1..000000000 --- a/FredBoat/src/main/java/fredboat/config/DatabaseConfiguration.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package fredboat.config; - -import com.zaxxer.hikari.metrics.prometheus.PrometheusMetricsTrackerFactory; -import fredboat.agent.DBConnectionWatchdogAgent; -import fredboat.agent.FredBoatAgent; -import fredboat.config.property.DatabaseConfig; -import fredboat.config.property.PropertyConfigProvider; -import fredboat.db.DatabaseManager; -import fredboat.main.ShutdownHandler; -import fredboat.shared.constant.BotConstants; -import fredboat.shared.constant.ExitCodes; -import fredboat.util.DiscordUtil; -import io.prometheus.client.hibernate.HibernateStatisticsCollector; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.springframework.orm.jpa.JpaVendorAdapter; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; -import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; -import space.npstr.sqlsauce.DatabaseConnection; -import space.npstr.sqlsauce.DatabaseWrapper; - -import javax.annotation.Nullable; - -/** - * Created by napster on 23.02.18. - *

- * Provides database related beans - */ -@Configuration -public class DatabaseConfiguration { - - private static final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class); - - @Primary - @Bean - public DatabaseWrapper mainDbWrapper(DatabaseConnection mainDbConn) { - return new DatabaseWrapper(mainDbConn); - } - - @Bean - @Nullable - public DatabaseWrapper cacheDbWrapper(@Nullable @Qualifier("cacheDbConn") DatabaseConnection cacheDbConn) { - return cacheDbConn == null ? null : new DatabaseWrapper(cacheDbConn); - } - - @Primary - @Bean - public DatabaseConnection mainDbConn(DatabaseManager databaseManager, ShutdownHandler shutdownHandler) - throws InterruptedException { - //attempt to connect to the database a few times - // this is relevant in a dockerized environment because after a reboot there is no guarantee that the db - // container will be started before the fredboat one - int dbConnectionAttempts = 0; - DatabaseConnection mainDbConn = null; - while ((mainDbConn == null || !mainDbConn.isAvailable()) && dbConnectionAttempts++ < 10) { - try { - if (mainDbConn != null) { - mainDbConn.shutdown(); - } - mainDbConn = databaseManager.getMainDbConn(); - } catch (Exception e) { - log.info("Could not connect to the database. Retrying in a moment...", e); - Thread.sleep(6000); - } - } - if (mainDbConn == null || !mainDbConn.isAvailable()) { - String message = "Could not establish database connection. Exiting..."; - log.error(message); - shutdownHandler.shutdown(ExitCodes.EXIT_CODE_ERROR); //a "hard" shutdown is kinda necessary in docker environments - throw new RuntimeException(message); - } - - FredBoatAgent.start(new DBConnectionWatchdogAgent(mainDbConn)); - return mainDbConn; - } - - @Bean - @Nullable - public DatabaseConnection cacheDbConn(DatabaseManager databaseManager, ShutdownHandler shutdownHandler) { - try { - return databaseManager.getCacheDbConn(); - } catch (Exception e) { - String message = "Exception when connecting to cache db"; - log.error(message, e); - shutdownHandler.shutdown(ExitCodes.EXIT_CODE_ERROR); //a "hard" shutdown is kinda necessary in docker environments - throw new RuntimeException(message); - } - } - - @Bean - public DatabaseManager databaseManager(PropertyConfigProvider configProvider, HibernateStatisticsCollector hibernateStats, - PrometheusMetricsTrackerFactory hikariStats) { - //run migrations except when its the patron boat - boolean migrateAndValidate = DiscordUtil.getBotId(configProvider.getCredentials()) != BotConstants.PATRON_BOT_ID; - - DatabaseConfig dbConf = configProvider.getDatabaseConfig(); - - DatabaseManager databaseManager = new DatabaseManager(hibernateStats, hikariStats, - dbConf.getHikariPoolSize(), configProvider.getAppConfig().getDistribution().name(), migrateAndValidate, - dbConf.getMainJdbcUrl(), dbConf.getMainSshTunnelConfig(), - dbConf.getCacheJdbcUrl(), dbConf.getCacheSshTunnelConfig(), - (puName, dataSource, properties, entityPackages) -> { - LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean(); - emfb.setDataSource(dataSource); - emfb.setPackagesToScan(entityPackages.toArray(new String[entityPackages.size()])); - - JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); - emfb.setJpaVendorAdapter(vendorAdapter); - emfb.setJpaProperties(properties); - - emfb.afterPropertiesSet(); //initiate creation of the native emf - return emfb.getNativeEntityManagerFactory(); - }); - - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - if (databaseManager.isCacheConnBuilt()) { - DatabaseConnection cacheDbConn = databaseManager.getCacheDbConn(); - if (cacheDbConn != null) { - cacheDbConn.shutdown(); - } - } - if (databaseManager.isMainConnBuilt()) { - databaseManager.getMainDbConn().shutdown(); - } - }, "databasemanager-shutdown-hook")); - - return databaseManager; - } -} diff --git a/FredBoat/src/main/java/fredboat/config/RepoConfiguration.java b/FredBoat/src/main/java/fredboat/config/RepoConfiguration.java index d4b7cf5d6..43a3727a6 100644 --- a/FredBoat/src/main/java/fredboat/config/RepoConfiguration.java +++ b/FredBoat/src/main/java/fredboat/config/RepoConfiguration.java @@ -24,14 +24,23 @@ package fredboat.config; +import com.google.gson.Gson; +import fredboat.config.property.BackendConfig; import fredboat.db.repositories.api.*; -import fredboat.db.repositories.impl.*; -import org.springframework.beans.factory.annotation.Qualifier; +import fredboat.db.repositories.impl.rest.*; +import fredboat.main.BotController; +import fredboat.main.ShutdownHandler; +import fredboat.shared.constant.ExitCodes; +import fredboat.util.rest.Http; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import space.npstr.sqlsauce.DatabaseWrapper; import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; /** * Created by napster on 24.02.18. @@ -41,39 +50,86 @@ @Configuration public class RepoConfiguration { + private static final Logger log = LoggerFactory.getLogger(RepoConfiguration.class); + + private final BackendConfig.Quarterdeck quarterdeckConfig; + private final Gson gson = new Gson(); + private final Http http = BotController.HTTP; //todo replace + + public RepoConfiguration(BackendConfig backendConfig, ShutdownHandler shutdownHandler) throws InterruptedException { + this.quarterdeckConfig = backendConfig.getQuarterdeck(); + + log.info("Contacting the quarterdeck backend"); + String[] apiVersions = null; + int attempts = 0; + Exception lastException = null; + while ((apiVersions == null || apiVersions.length < 1) && attempts < 100) { //total time is 100 sec + try { + String s = http.get(quarterdeckConfig.getHost() + "info/api/versions").auth(quarterdeckConfig.getBasicAuth()).asString(); + apiVersions = gson.fromJson(s, String[].class); + } catch (Exception ignored) { + lastException = ignored; + attempts++; + Thread.sleep(1000); + } + } + + if (apiVersions == null || apiVersions.length < 1) { + log.error("Could not contact the quarterdeck backend. Please make sure it is started and configuration values are correct", lastException); + shutdownHandler.shutdown(ExitCodes.EXIT_CODE_ERROR); + return; + } + + List supportedApiVersions = Arrays.stream(apiVersions).map(v -> { + if (!v.startsWith("v")) return "v" + v; + else return v; + }).collect(Collectors.toList()); + log.info("Supported Quarterdeck API versions: {}", String.join(", ", supportedApiVersions)); + + + String ourVersion = Integer.toString(RestRepo.API_VERSION); + if (supportedApiVersions.contains(ourVersion) + || supportedApiVersions.contains("v" + ourVersion)) { + log.info("Using Quarterdeck API v{}", ourVersion); + } else { + log.error("Quarterdeck API does not support our expected version v{}. Update quarterdeck, or roll back this FredBoat version!", ourVersion); + shutdownHandler.shutdown(ExitCodes.EXIT_CODE_ERROR); + } + } + @Bean - public BlacklistRepo blacklistRepo(DatabaseWrapper mainDbWrapper) { - return new SqlSauceBlacklistRepo(mainDbWrapper); + public BlacklistRepo blacklistRepo() { + return new RestBlacklistRepo(quarterdeckConfig.getHost(), http, gson, quarterdeckConfig.getBasicAuth()); } @Bean - public GuildConfigRepo guildConfigRepo(DatabaseWrapper mainDbWrapper) { - return new SqlSauceGuildConfigRepo(mainDbWrapper); + public GuildConfigRepo guildConfigRepo() { + return new RestGuildConfigRepo(quarterdeckConfig.getHost(), http, gson, quarterdeckConfig.getBasicAuth()); } @Bean - public GuildDataRepo guildDataRepo(DatabaseWrapper mainDbWrapper) { - return new SqlSauceGuildDataRepo(mainDbWrapper); + public GuildDataRepo guildDataRepo() { + return new RestGuildDataRepo(quarterdeckConfig.getHost(), http, gson, quarterdeckConfig.getBasicAuth()); } @Bean - public GuildModulesRepo guildModulesRepo(DatabaseWrapper mainDbWrapper) { - return new SqlSauceGuildModulesRepo(mainDbWrapper); + public GuildModulesRepo guildModulesRepo() { + return new RestGuildModulesRepo(quarterdeckConfig.getHost(), http, gson, quarterdeckConfig.getBasicAuth()); } @Bean - public GuildPermsRepo guildPermsRepo(DatabaseWrapper mainDbWrapper) { - return new SqlSauceGuildPermsRepo(mainDbWrapper); + public GuildPermsRepo guildPermsRepo() { + return new RestGuildPermsRepo(quarterdeckConfig.getHost(), http, gson, quarterdeckConfig.getBasicAuth()); } @Bean - public PrefixRepo prefixRepo(DatabaseWrapper mainDbWrapper) { - return new SqlSaucePrefixRepo(mainDbWrapper); + public PrefixRepo prefixRepo() { + return new RestPrefixRepo(quarterdeckConfig.getHost(), http, gson, quarterdeckConfig.getBasicAuth()); } @Nullable @Bean - public SearchResultRepo searchResultRepo(@Nullable @Qualifier("cacheDbWrapper") DatabaseWrapper cacheDbWrapper) { - return cacheDbWrapper == null ? null : new SqlSauceSearchResultRepo(cacheDbWrapper); //todo noop repo for cache entities? + public SearchResultRepo searchResultRepo() { + return new RestSearchResultRepo(quarterdeckConfig.getHost(), http, gson, quarterdeckConfig.getBasicAuth()); } } diff --git a/FredBoat/src/main/java/fredboat/config/ShardManagerConfiguration.java b/FredBoat/src/main/java/fredboat/config/ShardManagerConfiguration.java index ddc71646f..778ca6adf 100644 --- a/FredBoat/src/main/java/fredboat/config/ShardManagerConfiguration.java +++ b/FredBoat/src/main/java/fredboat/config/ShardManagerConfiguration.java @@ -26,8 +26,8 @@ import com.sedmelluq.discord.lavaplayer.jdaudp.NativeAudioSendFactory; import fredboat.audio.player.AudioConnectionFacade; +import fredboat.config.property.ConfigPropertiesProvider; import fredboat.config.property.Credentials; -import fredboat.config.property.PropertyConfigProvider; import fredboat.event.EventListenerBoat; import fredboat.event.EventLogger; import fredboat.event.MusicPersistenceHandler; @@ -68,7 +68,7 @@ public SessionController getSessionController(Credentials credentials) { } @Bean - public ShardManager buildShardManager(PropertyConfigProvider configProvider, EventListenerBoat mainEventListener, + public ShardManager buildShardManager(ConfigPropertiesProvider configProvider, EventListenerBoat mainEventListener, AudioConnectionFacade audioConnectionFacade, SessionController sessionController, EventLogger eventLogger, JdaEventsMetricsListener jdaEventsMetricsListener, ShardReviveHandler shardReviveHandler, MusicPersistenceHandler musicPersistenceHandler, @@ -76,7 +76,7 @@ public ShardManager buildShardManager(PropertyConfigProvider configProvider, Eve DefaultShardManagerBuilder builder = new DefaultShardManagerBuilder() .setToken(configProvider.getCredentials().getBotToken()) - .setGame(Game.playing(configProvider.getAppConfig().getGame())) + .setGame(Game.playing(configProvider.getAppConfig().getStatus())) .setBulkDeleteSplittingEnabled(false) .setEnableShutdownHook(false) .setAudioEnabled(true) diff --git a/FredBoat/src/main/java/fredboat/config/property/AppConfig.java b/FredBoat/src/main/java/fredboat/config/property/AppConfig.java index 0d666d7d0..b8ff9c6dd 100644 --- a/FredBoat/src/main/java/fredboat/config/property/AppConfig.java +++ b/FredBoat/src/main/java/fredboat/config/property/AppConfig.java @@ -24,6 +24,7 @@ package fredboat.config.property; +import fredboat.commandmeta.CommandInitializer; import fredboat.shared.constant.DistributionEnum; import java.util.List; @@ -55,11 +56,25 @@ default String getPrefix() { boolean isRestServerEnabled(); - List getAdminIds(); + List getAdminIds(); boolean useAutoBlacklist(); + int getPlayerLimit(); + + /** + * @return empty string for default status + */ String getGame(); + default String getStatus() { + String game = getGame(); + if (game.isEmpty()) { + return "Say " + getPrefix() + CommandInitializer.HELP_COMM_NAME; + } else { + return game; + } + } + boolean getContinuePlayback(); } diff --git a/FredBoat/src/main/java/fredboat/config/property/AppConfigProperties.java b/FredBoat/src/main/java/fredboat/config/property/AppConfigProperties.java new file mode 100644 index 000000000..c2c09765c --- /dev/null +++ b/FredBoat/src/main/java/fredboat/config/property/AppConfigProperties.java @@ -0,0 +1,146 @@ +/* + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fredboat.config.property; + +import fredboat.audio.player.PlayerLimitManager; +import fredboat.shared.constant.DistributionEnum; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by napster on 03.03.18. + */ +@Component +@ConfigurationProperties(prefix = "config") +public class AppConfigProperties implements AppConfig { + + private static final Logger log = LoggerFactory.getLogger(AppConfigProperties.class); + + private boolean development = true; + private boolean patron = true; + + private String prefix = "<<"; + private boolean restServerEnabled = false; + private List botAdmins = new ArrayList<>(); + private boolean autoBlacklist = true; + private String game = ""; + private boolean continuePlayback = false; + + //undocumented + private int playerLimit = -1; + + private boolean distributionLogged = false; + + + @Override + public DistributionEnum getDistribution() { + DistributionEnum distribution = development ? DistributionEnum.DEVELOPMENT + : patron ? DistributionEnum.PATRON + : DistributionEnum.MUSIC; + if (!distributionLogged) { + log.info("Determined distribution: {}", distribution); + distributionLogged = true; + } + return distribution; + } + + @Override + public String getPrefix() { + return prefix; + } + + @Override + public boolean isRestServerEnabled() { + return restServerEnabled; + } + + @Override + public List getAdminIds() { + return botAdmins; + } + + @Override + public boolean useAutoBlacklist() { + return autoBlacklist; + } + + @Override + public String getGame() { + return game; + } + + @Override + public boolean getContinuePlayback() { + return continuePlayback; + } + + @Override + public int getPlayerLimit() { + return playerLimit; + } + + public void setDevelopment(boolean development) { + this.development = development; + } + + public void setPatron(boolean patron) { + this.patron = patron; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + log.info("Using prefix: {}", prefix); + } + + public void setRestServerEnabled(boolean restServerEnabled) { + this.restServerEnabled = restServerEnabled; + } + + public void setBotAdmins(List botAdmins) { + this.botAdmins = botAdmins; + } + + public void setAutoBlacklist(boolean autoBlacklist) { + this.autoBlacklist = autoBlacklist; + } + + public void setGame(String game) { + this.game = game; + } + + public void setContinuePlayback(boolean continuePlayback) { + this.continuePlayback = continuePlayback; + } + + public void setPlayerLimit(int playerLimit) { + this.playerLimit = playerLimit; + PlayerLimitManager.setLimit(playerLimit); //todo make the playlimitmanager a component + } +} diff --git a/FredBoat/src/main/java/fredboat/config/property/AudioSourcesConfigProperties.java b/FredBoat/src/main/java/fredboat/config/property/AudioSourcesConfigProperties.java new file mode 100644 index 000000000..6d855a6e0 --- /dev/null +++ b/FredBoat/src/main/java/fredboat/config/property/AudioSourcesConfigProperties.java @@ -0,0 +1,128 @@ +/* + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fredboat.config.property; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * Created by napster on 03.03.18. + */ +@Component +@ConfigurationProperties(prefix = "audio-sources") +public class AudioSourcesConfigProperties implements AudioSourcesConfig { + + // audio managers + private boolean youtubeEnabled = true; + private boolean soundcloudEnabled = true; + private boolean bandcampEnabled = true; + private boolean twitchEnabled = true; + private boolean vimeoEnabled = true; + private boolean mixerEnabled = true; + private boolean spotifyEnabled = true; + private boolean localEnabled = false; + private boolean httpEnabled = false; + + @Override + public boolean isYouTubeEnabled() { + return youtubeEnabled; + } + + @Override + public boolean isSoundCloudEnabled() { + return soundcloudEnabled; + } + + @Override + public boolean isBandCampEnabled() { + return bandcampEnabled; + } + + @Override + public boolean isTwitchEnabled() { + return twitchEnabled; + } + + @Override + public boolean isVimeoEnabled() { + return vimeoEnabled; + } + + @Override + public boolean isMixerEnabled() { + return mixerEnabled; + } + + @Override + public boolean isSpotifyEnabled() { + return spotifyEnabled; + } + + @Override + public boolean isLocalEnabled() { + return localEnabled; + } + + @Override + public boolean isHttpEnabled() { + return httpEnabled; + } + + public void setEnableYoutube(boolean youtubeEnabled) { + this.youtubeEnabled = youtubeEnabled; + } + + public void setEnableSoundcloud(boolean soundcloudEnabled) { + this.soundcloudEnabled = soundcloudEnabled; + } + + public void setEnableBandcamp(boolean bandcampEnabled) { + this.bandcampEnabled = bandcampEnabled; + } + + public void setEnableTwitch(boolean twitchEnabled) { + this.twitchEnabled = twitchEnabled; + } + + public void setEnableVimeo(boolean vimeoEnabled) { + this.vimeoEnabled = vimeoEnabled; + } + + public void setEnableMixer(boolean mixerEnabled) { + this.mixerEnabled = mixerEnabled; + } + + public void setEnableSpotify(boolean spotifyEnabled) { + this.spotifyEnabled = spotifyEnabled; + } + + public void setEnableLocal(boolean localEnabled) { + this.localEnabled = localEnabled; + } + + public void setEnableHttp(boolean httpEnabled) { + this.httpEnabled = httpEnabled; + } +} diff --git a/FredBoat/src/main/java/fredboat/config/property/BackendConfig.java b/FredBoat/src/main/java/fredboat/config/property/BackendConfig.java new file mode 100644 index 000000000..9475f63ec --- /dev/null +++ b/FredBoat/src/main/java/fredboat/config/property/BackendConfig.java @@ -0,0 +1,43 @@ +/* + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fredboat.config.property; + +/** + * Created by napster on 27.02.18. + */ +public interface BackendConfig { + + Quarterdeck getQuarterdeck(); + + interface Quarterdeck { + String getHost(); + + String getUser(); + + String getPass(); + + String getBasicAuth(); + } +} diff --git a/FredBoat/src/main/java/fredboat/config/property/BackendConfigProperties.java b/FredBoat/src/main/java/fredboat/config/property/BackendConfigProperties.java new file mode 100644 index 000000000..bc9a4bfe7 --- /dev/null +++ b/FredBoat/src/main/java/fredboat/config/property/BackendConfigProperties.java @@ -0,0 +1,122 @@ +/* + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fredboat.config.property; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * Created by napster on 03.03.18. + */ +@Component +@ConfigurationProperties(prefix = "backend") +public class BackendConfigProperties implements BackendConfig { + + private static final Logger log = LoggerFactory.getLogger(BackendConfigProperties.class); + + private Quarterdeck quarterdeck = new Quarterdeck(); + + @Override + public Quarterdeck getQuarterdeck() { + return quarterdeck; + } + + public void setQuarterdeck(Quarterdeck quarterdeck) { + this.quarterdeck = quarterdeck; + } + + public static class Quarterdeck implements BackendConfig.Quarterdeck { + + private String host = ""; + private String user = ""; + private String pass = ""; + private String auth = ""; + + @Override + public String getHost() { + return host; + } + + @Override + public String getUser() { + return user; + } + + @Override + public String getPass() { + return pass; + } + + @Override + public String getBasicAuth() { + if (auth.isEmpty()) { + auth = okhttp3.Credentials.basic(getUser(), getPass()); + } + return auth; + } + + public void setHost(String host) { + this.host = host; + //noinspection ConstantConditions + if (host == null || host.isEmpty()) { + if ("docker".equals(System.getenv("ENV"))) { + log.info("No quarterdeck host found, docker environment detected. Using default quarterdeck url"); + this.host = "http://quarterdeck:4269/"; + } else { + String message = "No quarterdeck host provided in a non-docker environment. FredBoat cannot work without quarterdeck."; + log.error(message); + throw new RuntimeException(message); + } + } + + //fix up a missing trailing slash + if (!this.host.endsWith("/")) { + this.host += "/"; + } + } + + public void setUser(String user) { + this.user = user; + //noinspection ConstantConditions + if (user == null || user.isEmpty()) { + String message = "No quarterdeck user provided."; + log.error(message); + throw new RuntimeException(message); + } + } + + public void setPass(String pass) { + this.pass = pass; + //noinspection ConstantConditions + if (pass == null || pass.isEmpty()) { + String message = "No quarterdeck pass provided."; + log.error(message); + throw new RuntimeException(message); + } + } + } +} diff --git a/FredBoat/src/main/java/fredboat/config/property/PropertyConfigProvider.java b/FredBoat/src/main/java/fredboat/config/property/ConfigPropertiesProvider.java similarity index 94% rename from FredBoat/src/main/java/fredboat/config/property/PropertyConfigProvider.java rename to FredBoat/src/main/java/fredboat/config/property/ConfigPropertiesProvider.java index a1bd51a48..e28c8304a 100644 --- a/FredBoat/src/main/java/fredboat/config/property/PropertyConfigProvider.java +++ b/FredBoat/src/main/java/fredboat/config/property/ConfigPropertiesProvider.java @@ -29,15 +29,15 @@ *

* Provide access to our property based configs */ -public interface PropertyConfigProvider { +public interface ConfigPropertiesProvider { AppConfig getAppConfig(); AudioSourcesConfig getAudioSourcesConfig(); - Credentials getCredentials(); + BackendConfig getBackendConfig(); - DatabaseConfig getDatabaseConfig(); + Credentials getCredentials(); EventLoggerConfig getEventLoggerConfig(); diff --git a/FredBoat/src/main/java/fredboat/config/property/Credentials.java b/FredBoat/src/main/java/fredboat/config/property/Credentials.java index 881430412..40c0fddd8 100644 --- a/FredBoat/src/main/java/fredboat/config/property/Credentials.java +++ b/FredBoat/src/main/java/fredboat/config/property/Credentials.java @@ -33,7 +33,7 @@ /** * Created by napster on 19.02.18. *

- * All of these are documented in depth in the credentials.yaml.example file (to help selfhosters) + * All of these are documented in depth in the fredboat.yaml.example file (to help selfhosters) */ public interface Credentials { @@ -53,7 +53,7 @@ public interface Credentials { default String getRandomGoogleKey() { List googleKeys = getGoogleKeys(); if (googleKeys.isEmpty()) { - throw new MessagingException("No Youtube API key detected. Please read the documentation of the credentials file on how to obtain one."); + throw new MessagingException("No Youtube API key detected. Please read the documentation of the fredboat.yaml file on how to obtain one."); } return googleKeys.get((int) Math.floor(Math.random() * getGoogleKeys().size())); } diff --git a/FredBoat/src/main/java/fredboat/config/property/CredentialsProperties.java b/FredBoat/src/main/java/fredboat/config/property/CredentialsProperties.java new file mode 100644 index 000000000..c45d1a178 --- /dev/null +++ b/FredBoat/src/main/java/fredboat/config/property/CredentialsProperties.java @@ -0,0 +1,163 @@ +/* + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fredboat.config.property; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by napster on 03.03.18. + */ +@Component +@ConfigurationProperties(prefix = "credentials") +public class CredentialsProperties implements Credentials { + + private static final Logger log = LoggerFactory.getLogger(CredentialsProperties.class); + + private String discordBotToken = ""; + private List googleApiKeys = new ArrayList<>(); + private String malUser = ""; + private String malPassword = ""; + private String imgurClientId = ""; + private String spotifyId = ""; + private String spotifySecret = ""; + private String openWeatherKey = ""; + private String sentryDsn = ""; + private String carbonKey = ""; + private String dikeUrl = ""; + + + @Override + public String getBotToken() { + return discordBotToken; + } + + @Override + public List getGoogleKeys() { + return googleApiKeys; + } + + @Override + public String getMalUser() { + return malUser; + } + + @Override + public String getMalPassword() { + return malPassword; + } + + @Override + public String getImgurClientId() { + return imgurClientId; + } + + @Override + public String getSpotifyId() { + return spotifyId; + } + + @Override + public String getSpotifySecret() { + return spotifySecret; + } + + @Override + public String getOpenWeatherKey() { + return openWeatherKey; + } + + @Override + public String getSentryDsn() { + return sentryDsn; + } + + @Override + public String getCarbonKey() { + return carbonKey; + } + + @Override + public String getDikeUrl() { + return dikeUrl; + } + + public void setDiscordBotToken(String discordBotToken) { + this.discordBotToken = discordBotToken; + //noinspection ConstantConditions + if (discordBotToken == null || discordBotToken.isEmpty()) { + throw new RuntimeException("No discord bot token provided." + + "\nMake sure to put a discord bot token into your fredboat.yaml file."); + } + } + + public void setGoogleApiKeys(List googleApiKeys) { + this.googleApiKeys = googleApiKeys; + if (googleApiKeys.isEmpty()) { + log.warn("No google API keys found. Some commands may not work, check the documentation."); + } + } + + public void setMalUser(String malUser) { + this.malUser = malUser; + } + + public void setMalPassword(String malPassword) { + this.malPassword = malPassword; + } + + public void setImgurClientId(String imgurClientId) { + this.imgurClientId = imgurClientId; + } + + public void setSpotifyId(String spotifyId) { + this.spotifyId = spotifyId; + } + + public void setSpotifySecret(String spotifySecret) { + this.spotifySecret = spotifySecret; + } + + public void setOpenWeatherKey(String openWeatherKey) { + this.openWeatherKey = openWeatherKey; + } + + public void setSentryDsn(String sentryDsn) { + this.sentryDsn = sentryDsn; + } + + public void setCarbonKey(String carbonKey) { + this.carbonKey = carbonKey; + } + + public void setDikeUrl(String dikeUrl) { + this.dikeUrl = dikeUrl; + } +} diff --git a/FredBoat/src/main/java/fredboat/config/property/DatabaseConfig.java b/FredBoat/src/main/java/fredboat/config/property/EventLoggerConfigProperties.java similarity index 50% rename from FredBoat/src/main/java/fredboat/config/property/DatabaseConfig.java rename to FredBoat/src/main/java/fredboat/config/property/EventLoggerConfigProperties.java index f53763f82..7581c8dea 100644 --- a/FredBoat/src/main/java/fredboat/config/property/DatabaseConfig.java +++ b/FredBoat/src/main/java/fredboat/config/property/EventLoggerConfigProperties.java @@ -24,44 +24,55 @@ package fredboat.config.property; -import space.npstr.sqlsauce.ssh.SshTunnel; - -import javax.annotation.Nullable; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; /** - * Created by napster on 19.02.18. + * Created by napster on 03.03.18. */ -public interface DatabaseConfig { +@Component +@ConfigurationProperties(prefix = "event-logger") +public class EventLoggerConfigProperties implements EventLoggerConfig { + + private String eventLogWebhook = ""; + private int eventLogInterval = 1; + private String guildStatsWebhook = ""; + private int guildStatsInterval = 60; - /** - * JdbcUrl of the main database - */ - String getMainJdbcUrl(); - /** - * @return may return null if no tunnel shall be created for the main database connection - */ - @Nullable - SshTunnel.SshDetails getMainSshTunnelConfig(); + @Override + public String getEventLogWebhook() { + return eventLogWebhook; + } - /** - * @return JdbcUrl of the cache database, may return null if no cache database was provided. - */ - @Nullable - String getCacheJdbcUrl(); + @Override + public int getEventLogInterval() { + return eventLogInterval; + } - /** - * @return may return null if no tunnel shall be created for the cache database connection - */ - @Nullable - SshTunnel.SshDetails getCacheSshTunnelConfig(); + @Override + public String getGuildStatsWebhook() { + return guildStatsWebhook; + } + + @Override + public int getGuildStatsInterval() { + return guildStatsInterval; + } + + public void setEventLogWebhook(String eventLogWebhook) { + this.eventLogWebhook = eventLogWebhook; + } + + public void setEventLogInterval(int eventLogInterval) { + this.eventLogInterval = eventLogInterval; + } + + public void setGuildStatsWebhook(String guildStatsWebhook) { + this.guildStatsWebhook = guildStatsWebhook; + } - /** - * @return database connection poolsize - */ - default int getHikariPoolSize() { - //more database connections don't help with performance, so use a value based on available cores, but not too low - //http://www.dailymotion.com/video/x2s8uec_oltp-performance-concurrent-mid-tier-connections_tech - return Math.max(4, Runtime.getRuntime().availableProcessors()); + public void setGuildStatsInterval(int guildStatsInterval) { + this.guildStatsInterval = guildStatsInterval; } } diff --git a/FredBoat/src/main/java/fredboat/config/property/FileConfig.java b/FredBoat/src/main/java/fredboat/config/property/FileConfig.java deleted file mode 100644 index 2207fc461..000000000 --- a/FredBoat/src/main/java/fredboat/config/property/FileConfig.java +++ /dev/null @@ -1,616 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package fredboat.config.property; - -import com.google.common.base.CharMatcher; -import com.google.common.base.Suppliers; -import fredboat.audio.player.PlayerLimitManager; -import fredboat.commandmeta.CommandInitializer; -import fredboat.shared.constant.DistributionEnum; -import org.apache.commons.io.FileUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.error.YAMLException; -import space.npstr.sqlsauce.ssh.SshTunnel; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Supplier; - -public class FileConfig implements AppConfig, AudioSourcesConfig, Credentials, EventLoggerConfig, DatabaseConfig, LavalinkConfig { - - private static final Logger log = LoggerFactory.getLogger(FileConfig.class); - - private static final Supplier configSupplier = Suppliers.memoizeWithExpiration(FileConfig::loadConfig, - 1, TimeUnit.MINUTES); - - //a fallback reference to the last sucessfulyy loaded config, in case editing the config files breaks them - @Nullable - private static FileConfig lastSuccessfulLoaded; - - //todo: trace down all callers of this and make sure they take advantage of a reloading config like - //todo not storing the values once retrieved and recreating objects on value changes where possible/feasible - public static FileConfig get() { - return FileConfig.configSupplier.get(); - } - - - private static FileConfig loadConfig() { - long nanoTime = System.nanoTime(); - FileConfig c; - try { - c = new FileConfig( - loadConfigFile("credentials"), - loadConfigFile("config") - ); - } catch (Exception e) { - if (lastSuccessfulLoaded != null) { - log.error("Reloading config file failed! Serving last successfully loaded one.", e); - return lastSuccessfulLoaded; - } - throw new RuntimeException("Could not load config files!", e); - } - log.debug("Loading config took {}ms", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - nanoTime)); - lastSuccessfulLoaded = c; - return c; - } - - //FileConfig - - private DistributionEnum distribution; - private String prefix; - private boolean restServerEnabled; - private List adminIds = new ArrayList<>(); - private boolean useAutoBlacklist; - private String game; - private boolean continuePlayback; - - // audio managers - private boolean youtubeAudio; - private boolean soundcloudAudio; - private boolean bandcampAudio; - private boolean twitchAudio; - private boolean vimeoAudio; - private boolean mixerAudio; - private boolean spotifyAudio; - private boolean localAudio; - private boolean httpAudio; - - - //Credentials - - // essential - private String botToken; - private List googleKeys = new ArrayList<>(); - - // apis - private String malUser; - private String malPassword; - private String imgurClientId; - private String spotifyId; - private String spotifySecret; - private String openWeatherKey; - - // main database + SSH tunnel - @Nonnull - private String jdbcUrl; - @Nullable - private SshTunnel.SshDetails mainSshTunnelConfig; - - // cache database + SSH tunnel - @Nullable - private String cacheJdbcUrl; - @Nullable - private SshTunnel.SshDetails cacheSshTunnelConfig; - - // misc - private String sentryDsn; - private List lavalinkHosts = new ArrayList<>(); - private String eventLogWebhook; - private int eventLogInterval; - private String guildStatsWebhook; - private int guildStatsInterval; - - - // Undocumented creds - private String carbonKey; - private String dikeUrl; - - - /** - * The config is regularly reloaded, it should not be doing any blocking calls. - */ - @SuppressWarnings("unchecked") - private FileConfig(File credentialsFile, File configFile) { - try { - Yaml yaml = new Yaml(); - String credsFileStr = FileUtils.readFileToString(credentialsFile, "UTF-8"); - String configFileStr = FileUtils.readFileToString(configFile, "UTF-8"); - //remove those pesky tab characters so a potential json file is YAML conform - credsFileStr = cleanTabs(credsFileStr, "credentials.yaml"); - configFileStr = cleanTabs(configFileStr, "config.yaml"); - - Map creds = yaml.load(credsFileStr); - Map config = yaml.load(configFileStr); - - //avoid null values, rather change them to empty strings - creds.keySet().forEach((String key) -> creds.putIfAbsent(key, "")); - config.keySet().forEach((String key) -> config.putIfAbsent(key, "")); - - - //Load FileConfig values - - // Determine distribution - if ((boolean) config.getOrDefault("development", true)) { - distribution = DistributionEnum.DEVELOPMENT; - } else if ((boolean) config.getOrDefault("patron", true)) { - distribution = DistributionEnum.PATRON; - } else { - distribution = DistributionEnum.MUSIC; - } - log.info("Determined distribution: " + distribution); - - prefix = (String) config.getOrDefault("prefix", DEFAULT_PREFIX); - log.info("Using prefix: " + prefix); - restServerEnabled = (boolean) config.getOrDefault("restServerEnabled", true); - - Object admins = config.get("admins"); - if (admins instanceof List) { - ((List) admins).forEach((Object str) -> adminIds.add(str + "")); - } else if (admins instanceof String) { - adminIds.add(admins + ""); - } - useAutoBlacklist = (boolean) config.getOrDefault("useAutoBlacklist", true); - game = (String) config.getOrDefault("game", ""); - continuePlayback = (boolean) config.getOrDefault("continuePlayback", false); - - //Modular audiomanagers - youtubeAudio = (Boolean) config.getOrDefault("enableYouTube", true); - soundcloudAudio = (Boolean) config.getOrDefault("enableSoundCloud", true); - bandcampAudio = (Boolean) config.getOrDefault("enableBandCamp", true); - twitchAudio = (Boolean) config.getOrDefault("enableTwitch", true); - vimeoAudio = (Boolean) config.getOrDefault("enableVimeo", true); - mixerAudio = (Boolean) config.getOrDefault("enableMixer", true); - spotifyAudio = (Boolean) config.getOrDefault("enableSpotify", true); - localAudio = (Boolean) config.getOrDefault("enableLocal", false); - httpAudio = (Boolean) config.getOrDefault("enableHttp", false); - - - //Load Credential values - Object token = creds.get("token"); - if (token instanceof String) { - botToken = (String) token; - } else { - Map tokens = (Map) token; - botToken = tokens.getOrDefault(distribution.getId(), ""); - } - if (botToken == null || botToken.isEmpty()) { - throw new RuntimeException("No discord bot token provided for the started distribution " + distribution - + "\nMake sure to put a " + distribution.getId() + " token in your credentials file."); - } - - Object gkeys = creds.get("googleServerKeys"); - if (gkeys instanceof List) { - ((List) gkeys).forEach((Object str) -> googleKeys.add((String) str)); - } else if (gkeys instanceof String) { - googleKeys.add((String) gkeys); - } else { - log.warn("No google API keys found. Some commands may not work, check the documentation."); - } - - // apis - malUser = (String) creds.getOrDefault("malUser", ""); - malPassword = (String) creds.getOrDefault("malPassword", ""); - - imgurClientId = (String) creds.getOrDefault("imgurClientId", ""); - - spotifyId = (String) creds.getOrDefault("spotifyId", ""); - spotifySecret = (String) creds.getOrDefault("spotifySecret", ""); - - openWeatherKey = (String) creds.getOrDefault("openWeatherKey", ""); - sentryDsn = (String) creds.getOrDefault("sentryDsn", ""); - - - // main database - jdbcUrl = (String) creds.getOrDefault("jdbcUrl", ""); - if (jdbcUrl == null || jdbcUrl.isEmpty()) { - if ("docker".equals(System.getenv("ENV"))) { - log.info("No main JDBC URL found, docker environment detected. Using default docker main JDBC url"); - jdbcUrl = "jdbc:postgresql://db:5432/fredboat?user=fredboat"; - } else { - String message = "No main jdbcUrl provided in a non-docker environment. FredBoat cannot work without a database."; - log.error(message); - throw new RuntimeException(message); - } - } - boolean useSshTunnel = (boolean) creds.getOrDefault("useSshTunnel", false); - if (useSshTunnel) { - //Parse host:port - String sshHostRaw = (String) creds.getOrDefault("sshHost", "localhost:22"); - String sshHost = sshHostRaw.split(":")[0]; - int sshPort; - try { - sshPort = Integer.parseInt(sshHostRaw.split(":")[1]); - } catch (Exception e) { - sshPort = 22; - } - String sshUser = (String) creds.getOrDefault("sshUser", "fredboat"); - String sshPrivateKeyFile = (String) creds.getOrDefault("sshPrivateKeyFile", "database.ppk"); - String sshKeyPassphrase = (String) creds.getOrDefault("sshKeyPassphrase", ""); - int tunnelLocalPort = (int) creds.getOrDefault("tunnelLocalPort", 9333);//9333 is a legacy port for backwards compatibility - String tunnelRemotePortKey = "tunnelRemotePort"; - if (creds.containsKey("forwardToPort")) {//legacy check - tunnelRemotePortKey = "forwardToPort"; - } - int tunnelRemotePort = (int) creds.getOrDefault(tunnelRemotePortKey, 5432); - - mainSshTunnelConfig = new SshTunnel.SshDetails(sshHost, sshUser) - .setKeyFile(sshPrivateKeyFile) - .setPassphrase(sshKeyPassphrase == null || sshKeyPassphrase.isEmpty() ? null : sshKeyPassphrase) - .setSshPort(sshPort) - .setLocalPort(tunnelLocalPort) - .setRemotePort(tunnelRemotePort); - } - - - // cache database - cacheJdbcUrl = (String) creds.getOrDefault("cacheJdbcUrl", ""); - if (cacheJdbcUrl == null || cacheJdbcUrl.isEmpty()) { - if ("docker".equals(System.getenv("ENV"))) { - log.info("No cache jdbcUrl found, docker environment detected. Using default docker cache JDBC url"); - cacheJdbcUrl = "jdbc:postgresql://db:5432/fredboat_cache?user=fredboat"; - } else { - log.warn("No cache jdbcUrl provided in a non-docker environment. This may lead to a degraded performance, " - + "especially in a high usage environment, or when using Spotify playlists."); - cacheJdbcUrl = null; - } - } - if (jdbcUrl.equals(cacheJdbcUrl)) { - log.warn("The main and cache jdbc urls may not point to the same database due to how flyway handles migrations. " - + "Please read (an updated version of) the credentials.yaml.example on configuring the cache jdbc url. " - + "The cache database will not be available in this execution of FredBoat. This may lead to a degraded performance, " - + "especially in a high usage environment, or when using Spotify playlists."); - cacheJdbcUrl = null; - } - boolean cacheUseSshTunnel = (boolean) creds.getOrDefault("cacheUseSshTunnel", false); - if (cacheUseSshTunnel) { - //Parse host:port - String cacheSshHostRaw = (String) creds.getOrDefault("cacheSshHost", "localhost:22"); - String cacheSshHost = cacheSshHostRaw.split(":")[0]; - int cacheSshPort; - try { - cacheSshPort = Integer.parseInt(cacheSshHostRaw.split(":")[1]); - } catch (Exception e) { - cacheSshPort = 22; - } - String cacheSshUser = (String) creds.getOrDefault("cacheSshUser", "fredboat"); - String cacheSshPrivateKeyFile = (String) creds.getOrDefault("cacheSshPrivateKeyFile", "database.ppk"); - String cacheSshKeyPassphrase = (String) creds.getOrDefault("cacheSshKeyPassphrase", ""); - int cacheTunnelLocalPort = (int) creds.getOrDefault("cacheTunnelLocalPort", 5433); - int cacheTunnelRemotePort = (int) creds.getOrDefault("cacheTunnelRemotePort", 5432); - - cacheSshTunnelConfig = new SshTunnel.SshDetails(cacheSshHost, cacheSshUser) - .setKeyFile(cacheSshPrivateKeyFile) - .setPassphrase(cacheSshKeyPassphrase == null || cacheSshKeyPassphrase.isEmpty() ? null : cacheSshKeyPassphrase) - .setSshPort(cacheSshPort) - .setLocalPort(cacheTunnelLocalPort) - .setRemotePort(cacheTunnelRemotePort); - } - - // misc - Object linkNodes = creds.get("lavalinkHosts"); - if (linkNodes != null) { - if (linkNodes instanceof Map) { - Map simpleNodes = (Map) linkNodes; - AtomicInteger nodeCounter = new AtomicInteger(0); - simpleNodes.forEach((host, pass) -> { - try { - String name = "Lavalink-Node#" + nodeCounter.getAndIncrement(); - URI uri = new URI(host); - lavalinkHosts.add(new LavalinkConfig.LavalinkHost(name, uri, pass)); - log.info("Lavalink node added: {} {}", name, uri); - } catch (URISyntaxException e) { - throw new RuntimeException("Failed parsing lavalink URI", e); - } - }); - } else { - List> namedNodes = (List>) linkNodes; - for (Map node : namedNodes) { - try { - String name = node.get("name"); - URI uri = new URI(node.get("host")); - lavalinkHosts.add(new LavalinkConfig.LavalinkHost(name, uri, node.get("pass"))); - } catch (URISyntaxException e) { - throw new RuntimeException("Failed parsing lavalink URI", e); - } - } - } - } - - eventLogWebhook = (String) creds.getOrDefault("eventLogWebhook", ""); - eventLogInterval = (int) creds.getOrDefault("eventLogInterval", 1); //minutes - guildStatsWebhook = (String) creds.getOrDefault("guildStatsWebhook", ""); - guildStatsInterval = (int) creds.getOrDefault("guildStatsInterval", 60); //minutes - - - // Undocumented creds - carbonKey = (String) creds.getOrDefault("carbonKey", ""); - dikeUrl = (String) creds.getOrDefault("dikeUrl", ""); - - - PlayerLimitManager.setLimit((Integer) config.getOrDefault("playerLimit", -1)); - } catch (IOException e) { - log.error("Failed to read config and or credentials files into strings.", e); - throw new RuntimeException("Failed to read config and or credentials files into strings.", e); - } catch (YAMLException | ClassCastException e) { - log.error("Could not parse the credentials and/or config yaml files! They are probably misformatted. " + - "Try using an online yaml validator.", e); - throw e; - } catch (Exception e) { - log.error("Could not init config", e); - throw e; - } - } - - /** - * @param name relative name of a config file, without the file extension - * @return a handle on the requested file - */ - private static File loadConfigFile(String name) throws IOException { - String path = "./" + name + ".yaml"; - File file = new File(path); - if (!file.exists() || file.isDirectory()) { - throw new FileNotFoundException("Could not find '" + path + "' file."); - } - return file; - } - - private static String cleanTabs(String content, String file) { - CharMatcher tab = CharMatcher.is('\t'); - if (tab.matchesAnyOf(content)) { - log.warn("{} contains tab characters! Trying a fix-up.", file); - return tab.replaceFrom(content, " "); - } else { - return content; - } - } - - - // ******************************************************************************** - // Config Getters - // ******************************************************************************** - - @Override - public DistributionEnum getDistribution() { - return distribution; - } - - @Override - public String getPrefix() { - return prefix; - } - - @Override - public boolean isRestServerEnabled() { - return restServerEnabled; - } - - @Override - public List getAdminIds() { - return adminIds; - } - - @Override - public boolean useAutoBlacklist() { - return useAutoBlacklist; - } - - @Override - public String getGame() { - if (game.isEmpty()) { - return "Say " + getPrefix() + CommandInitializer.HELP_COMM_NAME; - } else { - return game; - } - } - - @Override - public boolean getContinuePlayback() { - return continuePlayback; - } - - @Override - public boolean isYouTubeEnabled() { - return youtubeAudio; - } - - @Override - public boolean isSoundCloudEnabled() { - return soundcloudAudio; - } - - @Override - public boolean isBandCampEnabled() { - return bandcampAudio; - } - - @Override - public boolean isTwitchEnabled() { - return twitchAudio; - } - - @Override - public boolean isVimeoEnabled() { - return vimeoAudio; - } - - @Override - public boolean isMixerEnabled() { - return mixerAudio; - } - - @Override - public boolean isSpotifyEnabled() { - return spotifyAudio; - } - - @Override - public boolean isLocalEnabled() { - return localAudio; - } - - @Override - public boolean isHttpEnabled() { - return httpAudio; - } - - - // ******************************************************************************** - // Credentials Getters - // ******************************************************************************** - - @Override - public String getBotToken() { - return botToken; - } - - @Override - public List getGoogleKeys() { - return googleKeys; - } - - @Override - public String getMalUser() { - return malUser; - } - - @Override - public String getMalPassword() { - return malPassword; - } - - @Override - public String getImgurClientId() { - return imgurClientId; - } - - @Override - public String getSpotifyId() { - return spotifyId; - } - - @Override - public String getSpotifySecret() { - return spotifySecret; - } - - @Override - public String getOpenWeatherKey() { - return openWeatherKey; - } - - @Override - public String getSentryDsn() { - return sentryDsn; - } - - @Override - @Nonnull - public String getMainJdbcUrl() { - return jdbcUrl; - } - - @Override - @Nullable - public SshTunnel.SshDetails getMainSshTunnelConfig() { - return mainSshTunnelConfig; - } - - @Override - @Nullable - //may return null if no cache database was provided. - public String getCacheJdbcUrl() { - return cacheJdbcUrl; - } - - @Override - @Nullable - public SshTunnel.SshDetails getCacheSshTunnelConfig() { - return cacheSshTunnelConfig; - } - - @Override - public List getLavalinkHosts() { - return lavalinkHosts; - } - - @Override - public String getEventLogWebhook() { - return eventLogWebhook; - } - - //minutes - @Override - public int getEventLogInterval() { - return eventLogInterval; - } - - @Override - public String getGuildStatsWebhook() { - return guildStatsWebhook; - } - - //minutes - @Override - public int getGuildStatsInterval() { - return guildStatsInterval; - } - - - // ******************************************************************************** - // Derived and undocumented values - // ******************************************************************************** - - @Override - public String getCarbonKey() { - return carbonKey; - } - - @Override - public String getDikeUrl() { - return dikeUrl; - } -} diff --git a/FredBoat/src/main/java/fredboat/config/property/LavalinkConfig.java b/FredBoat/src/main/java/fredboat/config/property/LavalinkConfig.java index 35da65260..42d897808 100644 --- a/FredBoat/src/main/java/fredboat/config/property/LavalinkConfig.java +++ b/FredBoat/src/main/java/fredboat/config/property/LavalinkConfig.java @@ -25,6 +25,7 @@ package fredboat.config.property; import java.net.URI; +import java.net.URISyntaxException; import java.util.List; /** @@ -35,19 +36,13 @@ public interface LavalinkConfig { /** * @return Lavalink nodes */ - List getLavalinkHosts(); + List getNodes(); - class LavalinkHost { + class LavalinkNode { - private final String name; - private final URI uri; - private final String password; - - public LavalinkHost(String name, URI uri, String password) { - this.name = name; - this.uri = uri; - this.password = password; - } + private String name = ""; + private URI uri; + private String pass = ""; public String getName() { return name; @@ -58,7 +53,19 @@ public URI getUri() { } public String getPassword() { - return password; + return pass; + } + + public void setName(String name) { + this.name = name; + } + + public void setHost(String host) throws URISyntaxException { + this.uri = new URI(host); + } + + public void setPass(String pass) { + this.pass = pass; } } } diff --git a/FredBoat/src/main/java/fredboat/config/property/FilePropertyConfigProvider.java b/FredBoat/src/main/java/fredboat/config/property/LavalinkConfigProperties.java similarity index 61% rename from FredBoat/src/main/java/fredboat/config/property/FilePropertyConfigProvider.java rename to FredBoat/src/main/java/fredboat/config/property/LavalinkConfigProperties.java index f6d12537f..4e7be12a4 100644 --- a/FredBoat/src/main/java/fredboat/config/property/FilePropertyConfigProvider.java +++ b/FredBoat/src/main/java/fredboat/config/property/LavalinkConfigProperties.java @@ -24,46 +24,35 @@ package fredboat.config.property; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; +import java.util.ArrayList; +import java.util.List; + /** - * Created by napster on 23.02.18. - *

- * Proxy calls to our reloading property configs + * Created by napster on 03.03.18. */ @Component -public class FilePropertyConfigProvider implements PropertyConfigProvider { +@ConfigurationProperties(prefix = "lavalink") +public class LavalinkConfigProperties implements LavalinkConfig { - public FilePropertyConfigProvider() { - } + private static final Logger log = LoggerFactory.getLogger(LavalinkConfigProperties.class); - @Override - public AppConfig getAppConfig() { - return FileConfig.get(); - } + private List nodes = new ArrayList<>(); - @Override - public AudioSourcesConfig getAudioSourcesConfig() { - return FileConfig.get(); - } @Override - public Credentials getCredentials() { - return FileConfig.get(); + public List getNodes() { + return nodes; } - @Override - public DatabaseConfig getDatabaseConfig() { - return FileConfig.get(); - } - - @Override - public EventLoggerConfig getEventLoggerConfig() { - return FileConfig.get(); - } - - @Override - public LavalinkConfig getLavalinkConfig() { - return FileConfig.get(); + public void setNodes(List nodes) { + this.nodes = nodes; + for (LavalinkNode node : nodes) { + log.info("Lavalink node added: {} {}", node.getName(), node.getUri()); + } } } diff --git a/FredBoat/src/main/java/fredboat/config/property/SpringConfigPropertiesProvider.java b/FredBoat/src/main/java/fredboat/config/property/SpringConfigPropertiesProvider.java new file mode 100644 index 000000000..fbb924383 --- /dev/null +++ b/FredBoat/src/main/java/fredboat/config/property/SpringConfigPropertiesProvider.java @@ -0,0 +1,81 @@ +/* + * MIT License + * + * Copyright (c) 2017-2018 Frederik Ar. Mikkelsen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fredboat.config.property; + +import org.springframework.stereotype.Component; + +/** + * Created by napster on 03.03.18. + */ +@Component +public class SpringConfigPropertiesProvider implements ConfigPropertiesProvider { + + private final AppConfig appConfig; + private final BackendConfig backendConfig; + private final AudioSourcesConfig audioSourcesConfig; + private final Credentials credentials; + private final EventLoggerConfig eventLoggerConfig; + private final LavalinkConfig lavalinkConfig; + + public SpringConfigPropertiesProvider(AppConfig appConfig, BackendConfig backendConfig, AudioSourcesConfig audioSourcesConfig, + Credentials credentials, EventLoggerConfig eventLoggerConfig, LavalinkConfig lavalinkConfig) { + this.appConfig = appConfig; + this.backendConfig = backendConfig; + this.audioSourcesConfig = audioSourcesConfig; + this.credentials = credentials; + this.eventLoggerConfig = eventLoggerConfig; + this.lavalinkConfig = lavalinkConfig; + } + + @Override + public AppConfig getAppConfig() { + return appConfig; + } + + @Override + public AudioSourcesConfig getAudioSourcesConfig() { + return audioSourcesConfig; + } + + @Override + public BackendConfig getBackendConfig() { + return backendConfig; + } + + @Override + public Credentials getCredentials() { + return credentials; + } + + @Override + public EventLoggerConfig getEventLoggerConfig() { + return eventLoggerConfig; + } + + @Override + public LavalinkConfig getLavalinkConfig() { + return lavalinkConfig; + } +} diff --git a/FredBoat/src/main/java/fredboat/db/EntityIO.java b/FredBoat/src/main/java/fredboat/db/EntityService.java similarity index 85% rename from FredBoat/src/main/java/fredboat/db/EntityIO.java rename to FredBoat/src/main/java/fredboat/db/EntityService.java index 1babae946..a6c19d676 100644 --- a/FredBoat/src/main/java/fredboat/db/EntityIO.java +++ b/FredBoat/src/main/java/fredboat/db/EntityService.java @@ -25,11 +25,12 @@ package fredboat.db; -import fredboat.config.property.PropertyConfigProvider; +import fredboat.config.property.ConfigPropertiesProvider; import fredboat.db.api.*; import fredboat.db.entity.cache.SearchResult; import fredboat.db.entity.main.*; import fredboat.db.repositories.api.*; +import fredboat.db.repositories.impl.rest.BackendException; import fredboat.util.DiscordUtil; import fredboat.util.func.NonnullSupplier; import net.dv8tion.jda.core.entities.Guild; @@ -51,12 +52,12 @@ */ @SuppressWarnings("UnusedReturnValue") @Component -public class EntityIO implements BlacklistIO, GuildConfigIO, GuildDataIO, GuildModulesIO, GuildPermsIO, PrefixIO, - SearchResultIO { +public class EntityService implements BlacklistService, GuildConfigService, GuildDataService, GuildModulesService, GuildPermsService, PrefixService, + SearchResultService { - private static final Logger log = LoggerFactory.getLogger(EntityIO.class); + private static final Logger log = LoggerFactory.getLogger(EntityService.class); - private final PropertyConfigProvider configProvider; + private final ConfigPropertiesProvider configProvider; private final GuildConfigRepo guildConfigRepo; private final GuildDataRepo guildDataRepo; @@ -68,9 +69,9 @@ public class EntityIO implements BlacklistIO, GuildConfigIO, GuildDataIO, GuildM @Nullable private final SearchResultRepo searchResultRepo; - public EntityIO(PropertyConfigProvider configProvider, BlacklistRepo blacklistRepo, GuildConfigRepo guildConfigRepo, - GuildDataRepo guildDataRepo, GuildModulesRepo guildModulesRepo, GuildPermsRepo guildPermsRepo, - PrefixRepo prefixRepo, @Nullable SearchResultRepo searchResultRepo) { + public EntityService(ConfigPropertiesProvider configProvider, BlacklistRepo blacklistRepo, GuildConfigRepo guildConfigRepo, + GuildDataRepo guildDataRepo, GuildModulesRepo guildModulesRepo, GuildPermsRepo guildPermsRepo, + PrefixRepo prefixRepo, @Nullable SearchResultRepo searchResultRepo) { this.configProvider = configProvider; this.blacklistRepo = blacklistRepo; this.guildConfigRepo = guildConfigRepo; @@ -89,33 +90,33 @@ public EntityIO(PropertyConfigProvider configProvider, BlacklistRepo blacklistRe private static T fetchUserFriendly(NonnullSupplier operation) { try { return operation.get(); - } catch (DatabaseException e) { - log.error("EntityIO database operation failed", e); + } catch (DatabaseException | BackendException e) { + log.error("EntityService database operation failed", e); throw new DatabaseNotReadyException(e); } } /** - * Same as {@link EntityIO#fetchUserFriendly(NonnullSupplier)}, just with a nullable return. + * Same as {@link EntityService#fetchUserFriendly(NonnullSupplier)}, just with a nullable return. */ @Nullable private static T getUserFriendly(Supplier operation) { try { return operation.get(); - } catch (DatabaseException e) { - log.error("EntityIO database operation failed", e); + } catch (DatabaseException | BackendException e) { + log.error("EntityService database operation failed", e); throw new DatabaseNotReadyException(e); } } /** - * Same as {@link EntityIO#fetchUserFriendly(NonnullSupplier)}, just without returning anything + * Same as {@link EntityService#fetchUserFriendly(NonnullSupplier)}, just without returning anything */ private static void doUserFriendly(Runnable operation) { try { operation.run(); - } catch (DatabaseException e) { - log.error("EntityIO database operation failed", e); + } catch (DatabaseException | BackendException e) { + log.error("EntityService database operation failed", e); throw new DatabaseNotReadyException(e); } } @@ -225,7 +226,7 @@ public Optional getPrefix(GuildBotComposite id) { @Nullable public SearchResult merge(SearchResult searchResult) { if (searchResultRepo != null) { - return searchResultRepo.merge(searchResult); + return fetchUserFriendly(() -> searchResultRepo.merge(searchResult)); } else { return null; } diff --git a/FredBoat/src/main/java/fredboat/event/EventListenerBoat.java b/FredBoat/src/main/java/fredboat/event/EventListenerBoat.java index 1001e5d2f..4709a0b02 100644 --- a/FredBoat/src/main/java/fredboat/event/EventListenerBoat.java +++ b/FredBoat/src/main/java/fredboat/event/EventListenerBoat.java @@ -39,8 +39,8 @@ import fredboat.commandmeta.abs.CommandContext; import fredboat.config.SentryConfiguration; import fredboat.config.property.AppConfig; -import fredboat.db.api.GuildConfigIO; -import fredboat.db.api.GuildDataIO; +import fredboat.db.api.GuildConfigService; +import fredboat.db.api.GuildDataService; import fredboat.db.entity.main.GuildData; import fredboat.definitions.Module; import fredboat.definitions.PermissionLevel; @@ -95,14 +95,14 @@ public class EventListenerBoat extends AbstractEventListener { private final JdaEntityProvider jdaEntityProvider; private final Ratelimiter ratelimiter; private final AppConfig appConfig; - private final GuildDataIO guildDataIO; - private final GuildConfigIO guildConfigIO; + private final GuildDataService guildDataService; + private final GuildConfigService guildConfigService; public EventListenerBoat(CommandManager commandManager, CommandContextParser commandContextParser, PlayerRegistry playerRegistry, CacheMetricsCollector cacheMetrics, ShardStatsCounterProvider shardStatsCounterProvider, JdaEntityProvider jdaEntityProvider, - Ratelimiter ratelimiter, AppConfig appConfig, GuildDataIO guildDataIO, - GuildConfigIO guildConfigIO) { + Ratelimiter ratelimiter, AppConfig appConfig, GuildDataService guildDataService, + GuildConfigService guildConfigService) { this.commandManager = commandManager; this.commandContextParser = commandContextParser; this.playerRegistry = playerRegistry; @@ -110,8 +110,8 @@ public EventListenerBoat(CommandManager commandManager, CommandContextParser com this.jdaEntityProvider = jdaEntityProvider; this.ratelimiter = ratelimiter; this.appConfig = appConfig; - this.guildDataIO = guildDataIO; - this.guildConfigIO = guildConfigIO; + this.guildDataService = guildDataService; + this.guildConfigService = guildConfigService; cacheMetrics.addCache("messagesToDeleteIfIdDeleted", messagesToDeleteIfIdDeleted); } @@ -251,7 +251,7 @@ public void onPrivateMessageReceived(PrivateMessageReceivedEvent event) { } //quick n dirty bot admin / owner check - if (appConfig.getAdminIds().contains(event.getAuthor().getId()) + if (appConfig.getAdminIds().contains(event.getAuthor().getIdLong()) || DiscordUtil.getOwnerId(event.getJDA()) == event.getAuthor().getIdLong()) { //hack in / hardcode some commands; this is not meant to look clean @@ -305,7 +305,7 @@ private void checkForAutoResume(VoiceChannel joinedChannel, Member joined) { && player.getPlayingTrack() != null && joinedChannel.getMembers().contains(guild.getSelfMember()) && player.getHumanUsersInCurrentVC().size() > 0 - && guildConfigIO.fetchGuildConfig(guild).isAutoResume() + && guildConfigService.fetchGuildConfig(guild).isAutoResume() ) { player.setPause(false); TextChannel activeTextChannel = player.getActiveTextChannel(); @@ -386,7 +386,7 @@ private void sendHelloOnJoin(@Nonnull Guild guild) { //filter guilds that already received a hello message // useful for when discord trolls us with fake guild joins // or to prevent it send repeatedly due to kick and reinvite - GuildData gd = guildDataIO.fetchGuildData(guild); + GuildData gd = guildDataService.fetchGuildData(guild); if (gd.getTimestampHelloSent() > 0) { return; } @@ -408,6 +408,6 @@ private void sendHelloOnJoin(@Nonnull Guild guild) { //send actual hello message and persist on success CentralMessaging.sendMessage(channel, HelloCommand.getHello(guild), - __ -> guildDataIO.transformGuildData(guild, GuildData::helloSent)); + __ -> guildDataService.transformGuildData(guild, GuildData::helloSent)); } } diff --git a/FredBoat/src/main/java/fredboat/event/EventLogger.java b/FredBoat/src/main/java/fredboat/event/EventLogger.java index 22619a73c..a831c30e0 100644 --- a/FredBoat/src/main/java/fredboat/event/EventLogger.java +++ b/FredBoat/src/main/java/fredboat/event/EventLogger.java @@ -26,7 +26,6 @@ package fredboat.event; import fredboat.config.property.EventLoggerConfig; -import fredboat.config.property.PropertyConfigProvider; import fredboat.jda.ShardProvider; import fredboat.main.ShutdownHandler; import fredboat.messaging.CentralMessaging; @@ -180,12 +179,11 @@ private Runnable createShutdownHook(ShutdownHandler shutdownHandler) { } //actual constructor - public EventLogger(PropertyConfigProvider configProvider, ShutdownHandler shutdownHandler, ShardProvider shardProvider) { + public EventLogger(EventLoggerConfig eventLoggerConfig, ShutdownHandler shutdownHandler, ShardProvider shardProvider) { this.shardProvider = shardProvider; - EventLoggerConfig config = configProvider.getEventLoggerConfig(); Runtime.getRuntime().addShutdownHook(new Thread(createShutdownHook(shutdownHandler), EventLogger.class.getSimpleName() + " shutdownhook")); - String eventLoggerWebhookUrl = config.getEventLogWebhook(); + String eventLoggerWebhookUrl = eventLoggerConfig.getEventLogWebhook(); WebhookClient eventLoggerWebhook = null; if (!eventLoggerWebhookUrl.isEmpty()) { try { @@ -220,11 +218,11 @@ public EventLogger(PropertyConfigProvider configProvider, ShutdownHandler shutdo } catch (Exception e) { log.error("Failed to send shard status summary to event log webhook", e); } - }, 0, Math.max(config.getEventLogInterval(), 1), TimeUnit.MINUTES); + }, 0, Math.max(eventLoggerConfig.getEventLogInterval(), 1), TimeUnit.MINUTES); } - String guildStatsWebhookUrl = config.getGuildStatsWebhook(); + String guildStatsWebhookUrl = eventLoggerConfig.getGuildStatsWebhook(); WebhookClient guildStatsWebhook = null; if (!guildStatsWebhookUrl.isEmpty()) { try { @@ -251,7 +249,7 @@ public EventLogger(PropertyConfigProvider configProvider, ShutdownHandler shutdo } this.guildStatsWebhook = workingWebhook; - int interval = Math.max(config.getGuildStatsInterval(), 1); + int interval = Math.max(eventLoggerConfig.getGuildStatsInterval(), 1); if (this.guildStatsWebhook != null) { scheduler.scheduleAtFixedRate(() -> { try { diff --git a/FredBoat/src/main/java/fredboat/event/MusicPersistenceHandler.java b/FredBoat/src/main/java/fredboat/event/MusicPersistenceHandler.java index f6e62cdd4..ce005dc62 100644 --- a/FredBoat/src/main/java/fredboat/event/MusicPersistenceHandler.java +++ b/FredBoat/src/main/java/fredboat/event/MusicPersistenceHandler.java @@ -39,6 +39,7 @@ import fredboat.feature.I18n; import fredboat.jda.JdaEntityProvider; import fredboat.messaging.CentralMessaging; +import fredboat.shared.constant.DistributionEnum; import fredboat.shared.constant.ExitCodes; import net.dv8tion.jda.core.JDA; import net.dv8tion.jda.core.entities.Guild; @@ -189,7 +190,7 @@ public void handlePreShutdown(int code) { @Override public void onReady(ReadyEvent event) { //the current implementation of music persistence is not a good idea on big bots - if (credentials.getRecommendedShardCount() <= 10) { + if (credentials.getRecommendedShardCount() <= 10 && appConfig.getDistribution() != DistributionEnum.MUSIC) { try { reloadPlaylists(event.getJDA()); } catch (Exception e) { diff --git a/FredBoat/src/main/java/fredboat/feature/I18n.java b/FredBoat/src/main/java/fredboat/feature/I18n.java index 15b59d6c8..78b705bc2 100644 --- a/FredBoat/src/main/java/fredboat/feature/I18n.java +++ b/FredBoat/src/main/java/fredboat/feature/I18n.java @@ -96,7 +96,7 @@ public static ResourceBundle get(@Nullable Guild guild) { @Nonnull public static FredBoatLocale getLocale(@Nonnull Guild guild) { try { - return LANGS.getOrDefault(Launcher.getBotController().getEntityIO().fetchGuildConfig(guild).getLang(), DEFAULT); + return LANGS.getOrDefault(Launcher.getBotController().getGuildConfigService().fetchGuildConfig(guild).getLang(), DEFAULT); } catch (DatabaseNotReadyException e) { //don't log spam the full exceptions or logs return DEFAULT; @@ -110,7 +110,7 @@ public static void set(@Nonnull Guild guild, @Nonnull String lang) throws Langua if (!LANGS.containsKey(lang)) throw new LanguageNotSupportedException("Language not found"); - Launcher.getBotController().getEntityIO().transformGuildConfig(guild, config -> config.setLang(lang)); + Launcher.getBotController().getGuildConfigService().transformGuildConfig(guild, config -> config.setLang(lang)); } public static class FredBoatLocale { diff --git a/FredBoat/src/main/java/fredboat/main/BotController.java b/FredBoat/src/main/java/fredboat/main/BotController.java index 93dc4ab43..3f6ce691a 100644 --- a/FredBoat/src/main/java/fredboat/main/BotController.java +++ b/FredBoat/src/main/java/fredboat/main/BotController.java @@ -4,12 +4,9 @@ import fredboat.agent.FredBoatAgent; import fredboat.audio.player.AudioConnectionFacade; import fredboat.audio.player.PlayerRegistry; -import fredboat.config.property.AppConfig; -import fredboat.config.property.AudioSourcesConfig; -import fredboat.config.property.Credentials; -import fredboat.config.property.PropertyConfigProvider; -import fredboat.db.DatabaseManager; -import fredboat.db.EntityIO; +import fredboat.config.property.*; +import fredboat.db.EntityService; +import fredboat.db.api.*; import fredboat.event.EventListenerBoat; import fredboat.feature.metrics.BotMetrics; import fredboat.feature.metrics.Metrics; @@ -35,14 +32,13 @@ public class BotController { .eventListener(new OkHttpEventMetrics("default", Metrics.httpEventCounter)) .build()); - private final PropertyConfigProvider configProvider; + private final ConfigPropertiesProvider configProvider; private final AudioConnectionFacade audioConnectionFacade; private final ShardManager shardManager; //central event listener that all events by all shards pass through private final EventListenerBoat mainEventListener; private final ShutdownHandler shutdownHandler; - private final DatabaseManager databaseManager; - private final EntityIO entityIO; + private final EntityService entityService; private final PlayerRegistry playerRegistry; private final JdaEntityProvider jdaEntityProvider; private final BotMetrics botMetrics; @@ -51,9 +47,9 @@ public class BotController { private final Ratelimiter ratelimiter; - public BotController(PropertyConfigProvider configProvider, AudioConnectionFacade audioConnectionFacade, ShardManager shardManager, - EventListenerBoat eventListenerBoat, ShutdownHandler shutdownHandler, DatabaseManager databaseManager, - EntityIO entityIO, ExecutorService executor, HibernateStatisticsCollector hibernateStats, + public BotController(ConfigPropertiesProvider configProvider, AudioConnectionFacade audioConnectionFacade, ShardManager shardManager, + EventListenerBoat eventListenerBoat, ShutdownHandler shutdownHandler, + EntityService entityService, ExecutorService executor, HibernateStatisticsCollector hibernateStats, PlayerRegistry playerRegistry, JdaEntityProvider jdaEntityProvider, BotMetrics botMetrics, @Qualifier("loadAudioPlayerManager") AudioPlayerManager audioPlayerManager, Ratelimiter ratelimiter) { @@ -62,9 +58,10 @@ public BotController(PropertyConfigProvider configProvider, AudioConnectionFacad this.shardManager = shardManager; this.mainEventListener = eventListenerBoat; this.shutdownHandler = shutdownHandler; - this.databaseManager = databaseManager; - this.entityIO = entityIO; - hibernateStats.register(); //call this exactly once after all db connections have been created + this.entityService = entityService; + try { + hibernateStats.register(); //call this exactly once after all db connections have been created + } catch (IllegalStateException ignored) {}//can happen when using the REST repos this.executor = executor; this.playerRegistry = playerRegistry; this.jdaEntityProvider = jdaEntityProvider; @@ -83,6 +80,10 @@ public AudioSourcesConfig getAudioSourcesConfig() { return configProvider.getAudioSourcesConfig(); } + public BackendConfig getBackendConfig() { + return configProvider.getBackendConfig(); + } + public Credentials getCredentials() { return configProvider.getCredentials(); } @@ -95,10 +96,6 @@ public ShutdownHandler getShutdownHandler() { return shutdownHandler; } - public DatabaseManager getDatabaseManager() { - return databaseManager; - } - @Nonnull public ExecutorService getExecutor() { return executor; @@ -112,9 +109,24 @@ public ShardManager getShardManager() { return shardManager; } - @Nonnull - public EntityIO getEntityIO() { - return entityIO; + public GuildConfigService getGuildConfigService() { + return entityService; + } + + public GuildModulesService getGuildModulesService() { + return entityService; + } + + public GuildPermsService getGuildPermsService() { + return entityService; + } + + public PrefixService getPrefixService() { + return entityService; + } + + public SearchResultService getSearchResultService() { + return entityService; } public PlayerRegistry getPlayerRegistry() { diff --git a/FredBoat/src/main/java/fredboat/main/Launcher.java b/FredBoat/src/main/java/fredboat/main/Launcher.java index 4f259b4fe..8a0733302 100644 --- a/FredBoat/src/main/java/fredboat/main/Launcher.java +++ b/FredBoat/src/main/java/fredboat/main/Launcher.java @@ -12,7 +12,7 @@ import fredboat.commandmeta.CommandInitializer; import fredboat.commandmeta.CommandRegistry; import fredboat.config.SentryConfiguration; -import fredboat.config.property.PropertyConfigProvider; +import fredboat.config.property.ConfigPropertiesProvider; import fredboat.feature.I18n; import fredboat.feature.metrics.BotMetrics; import fredboat.feature.metrics.MetricsServletAdapter; @@ -32,6 +32,7 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; @@ -45,7 +46,6 @@ import space.npstr.sqlsauce.DatabaseException; import java.io.IOException; -import java.util.Objects; import java.util.concurrent.ExecutorService; /** @@ -77,7 +77,7 @@ public class Launcher implements ApplicationRunner { private static final Logger log = LoggerFactory.getLogger(Launcher.class); public static final long START_TIME = System.currentTimeMillis(); private static BotController BC; //temporary hack access to the bot context - private final PropertyConfigProvider configProvider; + private final ConfigPropertiesProvider configProvider; private final ExecutorService executor; private final MetricsServletAdapter metricsServlet; private final CacheMetricsCollector cacheMetrics; @@ -90,6 +90,7 @@ public class Launcher implements ApplicationRunner { private final VideoSelectionCache videoSelectionCache; private final ShardProvider shardProvider; private final GuildProvider guildProvider; + private final int apiPort; private final SentryConfiguration sentryConfiguration; public static void main(String[] args) throws IllegalArgumentException, DatabaseException { @@ -105,21 +106,22 @@ public static void main(String[] args) throws IllegalArgumentException, Database } log.info(getVersionInfo()); - String javaVersionMinor = null; + int javaVersionMajor = -1; try { - javaVersionMinor = System.getProperty("java.version").split("\\.")[1]; + javaVersionMajor = Runtime.version().major(); } catch (Exception e) { - log.error("Exception while checking if java 8", e); + log.error("Exception while checking if java 9", e); } - if (!Objects.equals(javaVersionMinor, "8")) { + if (javaVersionMajor != 9) { log.warn("\n\t\t __ ___ ___ _ _ ___ _ _ ___ \n" + "\t\t \\ \\ / /_\\ | _ \\ \\| |_ _| \\| |/ __|\n" + "\t\t \\ \\/\\/ / _ \\| / .` || || .` | (_ |\n" + "\t\t \\_/\\_/_/ \\_\\_|_\\_|\\_|___|_|\\_|\\___|\n" + "\t\t "); - log.warn("FredBoat only officially supports Java 8. You are running Java {}", System.getProperty("java.version")); + log.warn("FredBoat only officially supports Java 9. You are running Java {}", Runtime.version()); } + System.setProperty("spring.config.name", "fredboat"); System.setProperty("spring.main.web-application-type", "none"); //todo enable again after spark API is migrated SpringApplication.run(Launcher.class, args); } @@ -128,12 +130,12 @@ public static BotController getBotController() { return BC; } - public Launcher(BotController botController, PropertyConfigProvider configProvider, ExecutorService executor, + public Launcher(BotController botController, ConfigPropertiesProvider configProvider, ExecutorService executor, MetricsServletAdapter metricsServlet, CacheMetricsCollector cacheMetrics, PlayerRegistry playerRegistry, StatsAgent statsAgent, BotMetrics botMetrics, Weather weather, AudioConnectionFacade audioConnectionFacade, TrackSearcher trackSearcher, VideoSelectionCache videoSelectionCache, ShardProvider shardProvider, GuildProvider guildProvider, - SentryConfiguration sentryConfiguration) { + @Value("${server.port:" + API.DEFAULT_PORT + "}") int apiPort, SentryConfiguration sentryConfiguration) { Launcher.BC = botController; this.configProvider = configProvider; this.executor = executor; @@ -148,6 +150,7 @@ public Launcher(BotController botController, PropertyConfigProvider configProvid this.videoSelectionCache = videoSelectionCache; this.shardProvider = shardProvider; this.guildProvider = guildProvider; + this.apiPort = apiPort; this.sentryConfiguration = sentryConfiguration; } @@ -157,7 +160,7 @@ public void run(ApplicationArguments args) throws InterruptedException { I18n.start(); try { - API.start(playerRegistry, botMetrics, shardProvider); + API.start(playerRegistry, botMetrics, shardProvider, apiPort); } catch (Exception e) { log.info("Failed to ignite Spark, FredBoat API unavailable", e); } diff --git a/FredBoat/src/main/java/fredboat/perms/PermsUtil.java b/FredBoat/src/main/java/fredboat/perms/PermsUtil.java index 3fdc04727..8477fd8ae 100644 --- a/FredBoat/src/main/java/fredboat/perms/PermsUtil.java +++ b/FredBoat/src/main/java/fredboat/perms/PermsUtil.java @@ -55,7 +55,7 @@ public static PermissionLevel getPerms(Member member) { return member.hasPermission(Permission.MESSAGE_MANAGE) ? PermissionLevel.DJ : PermissionLevel.USER; } - GuildPermissions gp = Launcher.getBotController().getEntityIO().fetchGuildPermissions(member.getGuild()); + GuildPermissions gp = Launcher.getBotController().getGuildPermsService().fetchGuildPermissions(member.getGuild()); if (checkList(gp.getAdminList(), member)) return PermissionLevel.ADMIN; if (checkList(gp.getDjList(), member)) return PermissionLevel.DJ; @@ -90,9 +90,9 @@ public static boolean checkPermsWithFeedback(PermissionLevel minLevel, CommandCo */ private static boolean isBotAdmin(Member member) { boolean botAdmin = false; - for (String id : Launcher.getBotController().getAppConfig().getAdminIds()) { + for (long id : Launcher.getBotController().getAppConfig().getAdminIds()) { Role r = member.getGuild().getRoleById(id); - if (member.getUser().getId().equals(id) + if (member.getUser().getIdLong() == id || (r != null && member.getRoles().contains(r))) { botAdmin = true; break; diff --git a/FredBoat/src/main/java/fredboat/util/ratelimit/Blacklist.java b/FredBoat/src/main/java/fredboat/util/ratelimit/Blacklist.java index bc56393a5..2ec6b54e1 100644 --- a/FredBoat/src/main/java/fredboat/util/ratelimit/Blacklist.java +++ b/FredBoat/src/main/java/fredboat/util/ratelimit/Blacklist.java @@ -24,7 +24,7 @@ package fredboat.util.ratelimit; -import fredboat.db.api.BlacklistIO; +import fredboat.db.api.BlacklistService; import fredboat.db.entity.main.BlacklistEntry; import fredboat.feature.metrics.Metrics; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; @@ -66,14 +66,14 @@ public class Blacklist { //users that can never be blacklisted private final Set userWhiteList; - private final BlacklistIO blacklistIO; + private final BlacklistService blacklistService; - public Blacklist(BlacklistIO blacklistIO, Set userWhiteList, long rateLimitHitsBeforeBlacklist) { - this.blacklistIO = blacklistIO; + public Blacklist(BlacklistService blacklistService, Set userWhiteList, long rateLimitHitsBeforeBlacklist) { + this.blacklistService = blacklistService; this.blacklist = new Long2ObjectOpenHashMap<>(); //load blacklist from database - for (BlacklistEntry ble : blacklistIO.loadBlacklist()) { + for (BlacklistEntry ble : blacklistService.loadBlacklist()) { blacklist.put(ble.id, ble); } @@ -140,7 +140,7 @@ public long hitRateLimit(long id) { } //persist it //if this turns up to be a performance bottleneck, have an agent run that persists the blacklist occasionally - blEntry = blacklistIO.mergeBlacklistEntry(blEntry); + blEntry = blacklistService.mergeBlacklistEntry(blEntry); blacklist.put(blEntry.id, blEntry); return blacklistingLength; } @@ -166,7 +166,7 @@ private synchronized BlacklistEntry getOrCreateBlacklistEntry(long id) { */ public synchronized void liftBlacklist(long id) { blacklist.remove(id); - blacklistIO.deleteBlacklistEntry(id); + blacklistService.deleteBlacklistEntry(id); } /** diff --git a/FredBoat/src/main/java/fredboat/util/ratelimit/Ratelimiter.java b/FredBoat/src/main/java/fredboat/util/ratelimit/Ratelimiter.java index 94be4b3c0..e7c3b11f0 100644 --- a/FredBoat/src/main/java/fredboat/util/ratelimit/Ratelimiter.java +++ b/FredBoat/src/main/java/fredboat/util/ratelimit/Ratelimiter.java @@ -30,7 +30,7 @@ import fredboat.command.util.WeatherCommand; import fredboat.commandmeta.abs.Command; import fredboat.config.property.AppConfig; -import fredboat.db.api.BlacklistIO; +import fredboat.db.api.BlacklistService; import fredboat.feature.metrics.Metrics; import fredboat.messaging.internal.Context; import fredboat.util.Tuple2; @@ -59,19 +59,17 @@ public class Ratelimiter { @Nullable private Blacklist autoBlacklist = null; - public Ratelimiter(AppConfig appConfig, ExecutorService executor, BlacklistIO blacklistIO) { + public Ratelimiter(AppConfig appConfig, ExecutorService executor, BlacklistService blacklistService) { Set whitelist = ConcurrentHashMap.newKeySet(); //only works for those admins who are added with their userId and not through a roleId - for (String admin : appConfig.getAdminIds()) { - whitelist.add(Long.valueOf(admin)); - } + whitelist.addAll(appConfig.getAdminIds()); //Create all the rate limiters we want ratelimits = new ArrayList<>(); if (appConfig.useAutoBlacklist()) { - autoBlacklist = new Blacklist(blacklistIO, whitelist, RATE_LIMIT_HITS_BEFORE_BLACKLIST); + autoBlacklist = new Blacklist(blacklistService, whitelist, RATE_LIMIT_HITS_BEFORE_BLACKLIST); } //sort these by harsher limits coming first diff --git a/FredBoat/src/main/java/fredboat/util/rest/SpotifyAPIWrapper.java b/FredBoat/src/main/java/fredboat/util/rest/SpotifyAPIWrapper.java index 16eea4f81..2f5eb6282 100644 --- a/FredBoat/src/main/java/fredboat/util/rest/SpotifyAPIWrapper.java +++ b/FredBoat/src/main/java/fredboat/util/rest/SpotifyAPIWrapper.java @@ -76,6 +76,9 @@ public SpotifyAPIWrapper(Credentials credentials) { * https://developer.spotify.com/web-api/authorization-guide/#client-credentials-flow */ private void refreshAccessToken() { + if (credentials.getSpotifyId().isEmpty() || credentials.getSpotifySecret().isEmpty()) { + return; //no spotify credentials configured, dont throw unnecessary errors + } try { JSONObject jsonClientCredentials = BotController.HTTP.post(URL_SPOTIFY_AUTHENTICATION_HOST + "/api/token", Http.Params.of( diff --git a/FredBoat/src/main/java/fredboat/util/rest/TrackSearcher.java b/FredBoat/src/main/java/fredboat/util/rest/TrackSearcher.java index 29a382f3b..30208d9b2 100644 --- a/FredBoat/src/main/java/fredboat/util/rest/TrackSearcher.java +++ b/FredBoat/src/main/java/fredboat/util/rest/TrackSearcher.java @@ -117,7 +117,7 @@ public AudioPlaylist searchForTracks(String query, long cacheMaxAge, int timeout if (!lavaplayerResult.getTracks().isEmpty()) { log.debug("Loaded search result {} {} from lavaplayer", provider, query); // got a search result? cache and return it - Launcher.getBotController().getExecutor().execute(() -> Launcher.getBotController().getEntityIO() + Launcher.getBotController().getExecutor().execute(() -> Launcher.getBotController().getSearchResultService() .merge(new SearchResult(audioPlayerManager, provider, query, lavaplayerResult))); Metrics.searchHits.labels("lavaplayer-" + provider.name().toLowerCase()).inc(); return lavaplayerResult; @@ -142,7 +142,7 @@ public AudioPlaylist searchForTracks(String query, long cacheMaxAge, int timeout if (!youtubeApiResult.getTracks().isEmpty()) { log.debug("Loaded search result {} {} from Youtube API", provider, query); // got a search result? cache and return it - Launcher.getBotController().getExecutor().execute(() -> Launcher.getBotController().getEntityIO() + Launcher.getBotController().getExecutor().execute(() -> Launcher.getBotController().getSearchResultService() .merge(new SearchResult(audioPlayerManager, provider, query, youtubeApiResult))); Metrics.searchHits.labels("youtube-api").inc(); return youtubeApiResult; @@ -171,7 +171,7 @@ public AudioPlaylist searchForTracks(String query, long cacheMaxAge, int timeout private AudioPlaylist fromCache(SearchProvider provider, String searchTerm, long cacheMaxAge) { try { SearchResult.SearchResultId id = new SearchResult.SearchResultId(provider, searchTerm); - SearchResult searchResult = Launcher.getBotController().getEntityIO().getSearchResult(id, cacheMaxAge); + SearchResult searchResult = Launcher.getBotController().getSearchResultService().getSearchResult(id, cacheMaxAge); return searchResult != null ? searchResult.getSearchResult(audioPlayerManager) : null; } catch (DatabaseNotReadyException ignored) { log.warn("Could not retrieve cached search result from database."); diff --git a/Shared/src/main/resources/logback.xml b/FredBoat/src/main/resources/logback.xml similarity index 100% rename from Shared/src/main/resources/logback.xml rename to FredBoat/src/main/resources/logback.xml diff --git a/FredBoat/src/test/java/fredboat/test/MockConfig.java b/FredBoat/src/test/java/fredboat/test/MockConfig.java index 556da174e..c37462876 100644 --- a/FredBoat/src/test/java/fredboat/test/MockConfig.java +++ b/FredBoat/src/test/java/fredboat/test/MockConfig.java @@ -30,9 +30,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yaml.snakeyaml.Yaml; -import space.npstr.sqlsauce.ssh.SshTunnel; -import javax.annotation.Nullable; import java.io.File; import java.util.Collections; import java.util.List; @@ -43,7 +41,7 @@ *

* A default fake config to be used in tests. */ -public class MockConfig implements AppConfig, AudioSourcesConfig, Credentials, DatabaseConfig, EventLoggerConfig, +public class MockConfig implements AppConfig, AudioSourcesConfig, Credentials, EventLoggerConfig, LavalinkConfig, TestingConfig { private static final Logger log = LoggerFactory.getLogger(MockConfig.class); @@ -56,7 +54,7 @@ public class MockConfig implements AppConfig, AudioSourcesConfig, Credentials, D public MockConfig() { try { Yaml yaml = new Yaml(); - String credsFileStr = FileUtils.readFileToString(new File("credentials.yaml"), "UTF-8"); + String credsFileStr = FileUtils.readFileToString(new File("fredboat.yaml"), "UTF-8"); Map creds = yaml.load(credsFileStr); creds.keySet().forEach((String key) -> creds.putIfAbsent(key, "")); @@ -95,7 +93,7 @@ public boolean isRestServerEnabled() { } @Override - public List getAdminIds() { + public List getAdminIds() { return Collections.emptyList(); } @@ -114,6 +112,11 @@ public boolean getContinuePlayback() { return false; } + @Override + public int getPlayerLimit() { + return -1; + } + @Override public boolean isYouTubeEnabled() { return false; @@ -205,30 +208,7 @@ public String getSentryDsn() { } @Override - public String getMainJdbcUrl() { - return ""; - } - - @Nullable - @Override - public SshTunnel.SshDetails getMainSshTunnelConfig() { - return null; - } - - @Nullable - @Override - public String getCacheJdbcUrl() { - return null; - } - - @Nullable - @Override - public SshTunnel.SshDetails getCacheSshTunnelConfig() { - return null; - } - - @Override - public List getLavalinkHosts() { + public List getNodes() { return Collections.emptyList(); } @@ -252,11 +232,6 @@ public int getGuildStatsInterval() { return 1; } - @Override - public int getHikariPoolSize() { - return 4; - } - @Override public String getCarbonKey() { return ""; diff --git a/Shared/build.gradle b/Shared/build.gradle index 39e33f9c8..2381b216a 100644 --- a/Shared/build.gradle +++ b/Shared/build.gradle @@ -3,6 +3,32 @@ version '1.0' ext { moduleName = 'Shared' } + + +apply plugin: 'maven-publish' + +publishing { + publications { + mavenJava(MavenPublication) { + groupId rootProject.group + artifactId moduleName + + from components.java + + artifact sourceJar { + classifier "sources" + } + } + } +} + +task install(dependsOn: 'publishToMavenLocal') +publishToMavenLocal.dependsOn 'jar' + +task sourceJar(type: Jar) { + from sourceSets.main.allJava +} + dependencies { compile group: 'org.json', name: 'json', version: jsonOrgVersion compile group: 'com.github.spotbugs', name: 'spotbugs-annotations', version: spotbugsVersion @@ -19,6 +45,8 @@ dependencies { compile group: 'com.squareup.okhttp3', name: 'okhttp', version: okhttpVersion + compile group: 'com.google.guava', name: 'guava', version: guavaVersion + compile group: 'io.prometheus', name: 'simpleclient', version: prometheusClientVersion compile group: 'io.prometheus', name: 'simpleclient_hotspot', version: prometheusClientVersion compile group: 'io.prometheus', name: 'simpleclient_logback', version: prometheusClientVersion diff --git a/FredBoat/src/main/java/fredboat/util/rest/CacheUtil.java b/Shared/src/main/java/fredboat/util/rest/CacheUtil.java similarity index 100% rename from FredBoat/src/main/java/fredboat/util/rest/CacheUtil.java rename to Shared/src/main/java/fredboat/util/rest/CacheUtil.java diff --git a/Shared/src/main/java/fredboat/util/rest/Http.java b/Shared/src/main/java/fredboat/util/rest/Http.java index 6882c8d89..5b356d2a3 100644 --- a/Shared/src/main/java/fredboat/util/rest/Http.java +++ b/Shared/src/main/java/fredboat/util/rest/Http.java @@ -92,6 +92,14 @@ public SimpleRequest get(@Nonnull String url) { .url(url)); } + @Nonnull + @CheckReturnValue + public SimpleRequest delete(@Nonnull String url) { + return new SimpleRequest(new Request.Builder() + .delete() + .url(url)); + } + @Nonnull @CheckReturnValue @@ -154,12 +162,20 @@ public SimpleRequest auth(@Nonnull String value) { return this; } + //set a basic authorization header + @Nonnull + @CheckReturnValue + public SimpleRequest basicAuth(@Nonnull String user, @Nonnull String pass) { + requestBuilder.header("Authorization", Credentials.basic(user, pass)); + return this; + } + //remember to close the response @Nonnull @CheckReturnValue public Response execute() throws IOException { Request req = requestBuilder.build(); - log.debug("{} {}", req.method(), req.url().toString()); + log.debug("{} {} {}", req.method(), req.url().toString(), req.body() != null ? req.body() : ""); return httpClient.newCall(req).execute(); } diff --git a/build.gradle b/build.gradle index 06351f017..3e076987e 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,6 @@ subprojects { //plugin versions gradleGitVersion = '1.4.21' - jdactionVersion = '1.0.2' shadowVersion = '2.0.2' springBootVersion = '2.0.0.RELEASE' propDepsVersion = '0.0.9.RELEASE' @@ -36,7 +35,6 @@ subprojects { } dependencies { classpath "gradle.plugin.com.gorylenko.gradle-git-properties:gradle-git-properties:${gradleGitVersion}" - classpath "gradle.plugin.com.sedmelluq:jdaction:${jdactionVersion}" classpath "com.github.jengelman.gradle.plugins:shadow:${shadowVersion}" classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}" classpath "io.spring.gradle:propdeps-plugin:${propDepsVersion}" @@ -44,8 +42,8 @@ subprojects { } apply plugin: 'java' - sourceCompatibility = 1.8 - targetCompatibility = 1.8 + sourceCompatibility = 9 + targetCompatibility = 9 compileJava.dependsOn 'clean' compileJava.options.encoding = 'UTF-8' @@ -82,6 +80,7 @@ subprojects { togglzVersion = '2.5.0.Final' guavaVersion = '24.0-jre' bucket4jVersion = '3.1.1' + gsonVersion = '2.8.2' //logging / monitoring deps logbackVersion = '1.2.3' @@ -97,10 +96,11 @@ subprojects { napsterAnnotations = '0.0.1' //database deps - sqlsauceVersion = '0.0.11' + sqlsauceVersion = '0.0.13' hibernateVersion = '5.2.14.Final' flywayVersion = '5.0.7' dsProxyVersion = '1.4.7' + jaxbApiVersion = '2.3.0' //testing deps junitVersion = '5.1.0' @@ -111,7 +111,7 @@ subprojects { } } -version = '2.3' +version = '3.0' ext { moduleName = 'FredBoat-Root' } diff --git a/docker-compose.yml b/docker-compose.yml index 59a72975a..ce5a9c4e9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -33,31 +33,49 @@ services: #volumes: #- postgres-data-volume:/var/lib/postgresql/data - ################################################################################ - ## FredBoat + ## Quarterdeck (Database Backend) ################################################################################ - bot: + quarterdeck: # pick one of these: stable or dev # dev receives more frequent updates than stable but may have more bugs / things breaking # versions (example: v2) receive continous non-breaking (best effort) updates via watchtower. switching between versions # (example: going from v2 to v3) usually requires manual migration. what exactly is needed is published on the # FredBoat selfhosting website and/or the selfhosters channel (see top of this file for how to get to these places) - image: fredboat/fredboat:stable-v2 - #image: fredboat/fredboat:dev-v2 + # Once you use the dev branch, you may not be able to go back to stable without deleting your database. + image: fredboat/quarterdeck:stable-v1 + #image: fredboat/quarterdeck:dev-v1 + restart: always + labels: + - "com.centurylinklabs.watchtower.enable=true" + depends_on: + - db + volumes: + - ./quarterdeck.yaml:/opt/Quarterdeck/quarterdeck.yaml + - ./quarterdeck_logs:/opt/Quarterdeck/logs + # Need a bigger memory size or any other custom JVM args? uncomment and edit the line below accordingly + #entrypoint: java -Xmx128m -jar Quarterdeck.jar + + ################################################################################ + ## FredBoat + ################################################################################ + bot: + # for choosing between stable or dev, read the paragraph above in the Quarterdeck section + # IMPORTANT: both quarterdeck and fredboat need to either be on the stable, or on the dev branch + image: fredboat/fredboat:stable-v3 + #image: fredboat/fredboat:dev-v3 #build: ./FredBoat #useful alternative for developers restart: on-failure:3 labels: - "com.centurylinklabs.watchtower.enable=true" depends_on: - - db + - quarterdeck ports: - 1356:1356 volumes: - - ./config.yaml:/opt/FredBoat/config.yaml - - ./credentials.yaml:/opt/FredBoat/credentials.yaml - - ./logs:/opt/FredBoat/logs + - ./fredboat.yaml:/opt/FredBoat/fredboat.yaml + - ./fredboat_logs:/opt/FredBoat/logs - ./music_persistence:/opt/FredBoat/music_persistence # Local audio files (dev branch only currently) # If your music directory looks like this: /home/user/music/directory/rickroll.mp3 or C:\Users\user\Music\rickroll.mp3 @@ -68,7 +86,7 @@ services: #- C:\Users\your_user_name\Music:/opt/FredBoat/music # Need a bigger memory size or any other custom JVM args? uncomment and edit the line below accordingly - #entrypoint: java -Xmx128m -jar FredBoat.jar + #entrypoint: java -Xmx256m -jar FredBoat.jar ################################################################################ diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 000000000..2e4852805 --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,2 @@ +jdk: + - oraclejdk9 \ No newline at end of file