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.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 = CopyOnWriteArraySet<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 (_: 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 (_: 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() { if (tv != null) { future?.cancel(false) tv?.quit() tv = null } else { logInfo("TV is already disposed") } } fun loadBannedTV() { bannedTVStore = ObjectMapper().readValue( Paths.get("Banned TV.json").toFile().absoluteFile, object : TypeReference<CopyOnWriteArraySet<String>>() {}) logInfo("Loaded Banned TV") saveBannedTV() } 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) } }