package net.taehui.twilight.system import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.ObjectMapper import net.taehui.twilight.Logger import org.openqa.selenium.By import org.openqa.selenium.TimeoutException import org.openqa.selenium.WebDriver import org.openqa.selenium.edge.EdgeDriver import org.openqa.selenium.edge.EdgeOptions import org.openqa.selenium.support.ui.ExpectedConditions import org.openqa.selenium.support.ui.WebDriverWait import java.io.IOException import java.nio.file.Files import java.nio.file.Paths import java.time.Duration import java.util.concurrent.* object TVSystem : Logger { class TVItem(val href: String, val title: String, val text: String) { override fun hashCode(): Int { return href.hashCode() } override fun equals(other: Any?): Boolean { return href == (other as TVItem).href } override fun toString(): String { return text } } private val ses: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor { Executors.defaultThreadFactory().newThread(it).apply { isDaemon = true } } private var tv: WebDriver? = null private var future: ScheduledFuture<*>? = null private var bannedTVStore = ConcurrentSkipListSet<String>() fun handleSystem() { if (tv != null) { logInfo("TV is already running") } else if (Configure.mode.tv) { try { tv = EdgeDriver(EdgeOptions().apply { addArguments("--headless", "--no-sandbox") }) val pendingElements = mutableMapOf<TVItem, Int>() var lastElements = emptySet<TVItem>() future = ses.scheduleWithFixedDelay({ tv?.let { it -> it.get("https://www.twitch.tv/directory/game/Qwilight") val test0 = By.cssSelector("[data-test-selector=TitleAndChannel]") val elements0 = try { WebDriverWait( it, Duration.ofSeconds(1) ).until(ExpectedConditions.visibilityOfAllElementsLocatedBy(test0)) it.findElements(test0) ?: throw TimeoutException() } catch (e: TimeoutException) { emptyList() }.map { element -> TVItem( element.getAttribute("href"), element.findElement(By.tagName("h3")).text, element.findElement(By.tagName("p")).text ) }.toSet() it.get("https://chzzk.naver.com/category/GAME/Qwilight") val test1 = By.cssSelector("#layout-body > div > section > div > ul > li > div > div > div:nth-child(2)") val elements1 = try { WebDriverWait( it, Duration.ofSeconds(1) ).until(ExpectedConditions.visibilityOfAllElementsLocatedBy(test1)) it.findElements(test1) } catch (e: TimeoutException) { emptyList() }.map { val titleElement = it.findElement(By.cssSelector("a")) TVItem( titleElement.getAttribute("href"), titleElement.text.split("\n")[0], it.findElement(By.cssSelector("div > a > span > span")).text ) }.toSet() val elements = elements0.plus(elements1).filter { !bannedTVStore.contains(it.text) }.toSet() elements.subtract(lastElements).filter { pendingElements.remove(it) == null }.forEach { SiteHandler.putSiteYell(it) } lastElements.subtract(elements).forEach { pendingElements[it] = 0 } pendingElements.toMap().forEach { if (it.value < 5) { pendingElements[it.key] = it.value + 1 } else { pendingElements.remove(it.key) } } lastElements = elements } }, 0L, 1L, TimeUnit.MINUTES) } catch (e: Exception) { logFault(e) } } else { logInfo("TV is disabled") } } val tvStatus: String get() = "{Title: ${tv?.title}, Page: ${tv?.pageSource}, URL: ${tv?.currentUrl}, Handles: [${tv?.windowHandles?.joinToString()}]}" fun dispose() { future?.cancel(false) tv?.quit() tv = null } fun loadBannedTV() { try { bannedTVStore.addAll( ObjectMapper().readValue( Paths.get("Banned TV.json").toFile().absoluteFile, object : TypeReference<ConcurrentSkipListSet<String>>() {}) ) logInfo("Loaded Banned TV") saveBannedTV() } catch (e: IOException) { logFault(e) } } fun saveBannedTV() { Files.newOutputStream(Paths.get("Banned TV.json")) .use { ObjectMapper().writerWithDefaultPrettyPrinter().writeValue(it, bannedTVStore) } logInfo("Saved Banned TV") } fun putBannedTV(tv: String) { bannedTVStore.add(tv) } fun wipeBannedTV(tv: String) { bannedTVStore.remove(tv) } }