diff --git a/src/en/warforrayuba/AndroidManifest.xml b/src/en/warforrayuba/AndroidManifest.xml
new file mode 100644
index 000000000..30deb7f79
--- /dev/null
+++ b/src/en/warforrayuba/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/src/en/warforrayuba/build.gradle b/src/en/warforrayuba/build.gradle
new file mode 100644
index 000000000..e3df25371
--- /dev/null
+++ b/src/en/warforrayuba/build.gradle
@@ -0,0 +1,16 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlinx-serialization'
+
+ext {
+ extName = 'War For Rayuba'
+ pkgNameSuffix = 'en.warforrayuba'
+ extClass = '.WarForRayuba'
+ extVersionCode = 1
+}
+
+dependencies {
+ implementation project(':lib-ratelimit')
+}
+
+apply from: "$rootDir/common.gradle"
diff --git a/src/en/warforrayuba/res/mipmap-hdpi/ic_launcher.png b/src/en/warforrayuba/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..93ca7ab25
Binary files /dev/null and b/src/en/warforrayuba/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/src/en/warforrayuba/res/mipmap-mdpi/ic_launcher.png b/src/en/warforrayuba/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..b541e1fdb
Binary files /dev/null and b/src/en/warforrayuba/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/src/en/warforrayuba/res/mipmap-xhdpi/ic_launcher.png b/src/en/warforrayuba/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..541244928
Binary files /dev/null and b/src/en/warforrayuba/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/src/en/warforrayuba/res/mipmap-xxhdpi/ic_launcher.png b/src/en/warforrayuba/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..fa6dff8b1
Binary files /dev/null and b/src/en/warforrayuba/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/src/en/warforrayuba/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/warforrayuba/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..d70951f95
Binary files /dev/null and b/src/en/warforrayuba/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/src/en/warforrayuba/res/web_hi_res_512.png b/src/en/warforrayuba/res/web_hi_res_512.png
new file mode 100644
index 000000000..959452e89
Binary files /dev/null and b/src/en/warforrayuba/res/web_hi_res_512.png differ
diff --git a/src/en/warforrayuba/src/eu/kanade/tachiyomi/extension/en/warforrayuba/WarForRayuba.kt b/src/en/warforrayuba/src/eu/kanade/tachiyomi/extension/en/warforrayuba/WarForRayuba.kt
new file mode 100644
index 000000000..9db8d8765
--- /dev/null
+++ b/src/en/warforrayuba/src/eu/kanade/tachiyomi/extension/en/warforrayuba/WarForRayuba.kt
@@ -0,0 +1,157 @@
+package eu.kanade.tachiyomi.extension.en.warforrayuba
+
+import android.os.Build
+import eu.kanade.tachiyomi.BuildConfig
+import eu.kanade.tachiyomi.extension.en.warforrayuba.dto.PageDto
+import eu.kanade.tachiyomi.extension.en.warforrayuba.dto.RoundDto
+import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.network.asObservableSuccess
+import eu.kanade.tachiyomi.source.model.FilterList
+import eu.kanade.tachiyomi.source.model.MangasPage
+import eu.kanade.tachiyomi.source.model.Page
+import eu.kanade.tachiyomi.source.model.SChapter
+import eu.kanade.tachiyomi.source.model.SManga
+import eu.kanade.tachiyomi.source.online.HttpSource
+import eu.kanade.tachiyomi.util.asJsoup
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
+import okhttp3.Headers
+import okhttp3.Request
+import okhttp3.Response
+import rx.Observable
+
+class WarForRayuba : HttpSource() {
+
+ override val name = "War For Rayuba"
+
+ override val baseUrl = "https://xrabohrok.github.io/WarMap/#/"
+
+ override val lang = "en"
+
+ override val supportsLatest = false
+
+ override val client = network.cloudflareClient.newBuilder()
+ .addNetworkInterceptor(RateLimitInterceptor(4)).build()
+
+ private val json = Json {
+ isLenient = true
+ ignoreUnknownKeys = true
+ allowSpecialFloatingPointValues = true
+ useArrayPolymorphism = true
+ prettyPrint = true
+ }
+
+ private val cubariHeaders = Headers.Builder().apply {
+ add(
+ "User-Agent",
+ "(Android ${Build.VERSION.RELEASE}; " +
+ "${Build.MANUFACTURER} ${Build.MODEL}) " +
+ "Tachiyomi/${BuildConfig.VERSION_NAME} " +
+ Build.ID
+ )
+ }.build()
+
+ override fun headersBuilder() = Headers.Builder().apply {
+ add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:86.0) Gecko/20100101 Firefox/86.0 ")
+ add("Referer", baseUrl)
+ }
+
+ override fun popularMangaRequest(page: Int) = GET("https://github.com/xrabohrok/WarMap/tree/main/tools", headers)
+
+ override fun popularMangaParse(response: Response): MangasPage {
+ val document = response.asJsoup()
+
+ val mangas = document.select("#repo-content-pjax-container .Details div[role=row] div[role=rowheader] a[href*='.json']").map { element ->
+ SManga.create().apply {
+ val githubRawUrl = "https://raw.githubusercontent.com/xrabohrok/WarMap/" + element.attr("abs:href").replace(".*(?=main)".toRegex(), "")
+ val githubData: RoundDto = json.decodeFromString(
+ client.newCall(GET(githubRawUrl, headers)).execute().body!!.string()
+ )
+
+ title = githubData.title
+ thumbnail_url = githubData.cover
+ url = githubRawUrl
+ }
+ }
+
+ return MangasPage(mangas, false)
+ }
+
+ override fun fetchMangaDetails(manga: SManga): Observable {
+ return client.newCall(apiMangaDetailsRequest(manga))
+ .asObservableSuccess()
+ .map { response ->
+ mangaDetailsParse(response).apply { initialized = true }
+ }
+ }
+
+ private fun apiMangaDetailsRequest(manga: SManga): Request {
+ return GET(manga.url, headers)
+ }
+
+ override fun mangaDetailsRequest(manga: SManga): Request {
+ return GET(baseUrl, headers)
+ }
+
+ override fun mangaDetailsParse(response: Response) = SManga.create().apply {
+ val githubData: RoundDto = json.decodeFromString(response.body!!.string())
+
+ thumbnail_url = githubData.cover
+ status = SManga.UNKNOWN
+ author = githubData.author
+ artist = githubData.artist
+ title = githubData.title
+ description = githubData.description
+ }
+
+ override fun chapterListRequest(manga: SManga): Request {
+ return GET(manga.url, headers)
+ }
+
+ override fun chapterListParse(response: Response): List {
+ val responseJson: RoundDto = json.decodeFromString(response.body!!.string())
+
+ val chapterList: MutableList = ArrayList()
+ responseJson.chapters.forEach { (number, chapter) ->
+ chapterList.add(
+ SChapter.create().apply {
+ url = "https://cubari.moe" + chapter.groups.primary
+ chapter_number = number.toFloat()
+ name = number.toString() + " " + chapter.title
+ date_upload = chapter.last_updated
+ }
+ )
+ }
+
+ return chapterList.reversed()
+ }
+
+ override fun pageListRequest(chapter: SChapter): Request {
+ return GET(chapter.url, cubariHeaders)
+ }
+
+ override fun pageListParse(response: Response): List {
+ val chapterData: List = json.decodeFromString(response.body!!.string())
+
+ val pageList = chapterData.mapIndexed { index, page ->
+ Page(index, page.src.slice(0..page.src.lastIndexOf(".")), page.src)
+ }
+
+ return pageList
+ }
+
+ override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable {
+ return Observable.just(MangasPage(emptyList(), false))
+ }
+
+ override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException("Not Used")
+
+ override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException("Not Used")
+
+ override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not Used")
+
+ override fun latestUpdatesParse(response: Response): MangasPage = throw UnsupportedOperationException("Not Used")
+
+ override fun searchMangaParse(response: Response): MangasPage = throw UnsupportedOperationException("Not Used")
+}
diff --git a/src/en/warforrayuba/src/eu/kanade/tachiyomi/extension/en/warforrayuba/dto/ChapterDto.kt b/src/en/warforrayuba/src/eu/kanade/tachiyomi/extension/en/warforrayuba/dto/ChapterDto.kt
new file mode 100644
index 000000000..02dae64d2
--- /dev/null
+++ b/src/en/warforrayuba/src/eu/kanade/tachiyomi/extension/en/warforrayuba/dto/ChapterDto.kt
@@ -0,0 +1,16 @@
+package eu.kanade.tachiyomi.extension.en.warforrayuba.dto
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class ChapterDto(
+ val title: String,
+ val volume: Int,
+ val groups: ChapterGroupDto,
+ val last_updated: Long,
+)
+
+@Serializable
+data class ChapterGroupDto(
+ val primary: String
+)
diff --git a/src/en/warforrayuba/src/eu/kanade/tachiyomi/extension/en/warforrayuba/dto/PageDto.kt b/src/en/warforrayuba/src/eu/kanade/tachiyomi/extension/en/warforrayuba/dto/PageDto.kt
new file mode 100644
index 000000000..ee496c862
--- /dev/null
+++ b/src/en/warforrayuba/src/eu/kanade/tachiyomi/extension/en/warforrayuba/dto/PageDto.kt
@@ -0,0 +1,9 @@
+package eu.kanade.tachiyomi.extension.en.warforrayuba.dto
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class PageDto(
+ val description: String,
+ val src: String
+)
diff --git a/src/en/warforrayuba/src/eu/kanade/tachiyomi/extension/en/warforrayuba/dto/RoundDto.kt b/src/en/warforrayuba/src/eu/kanade/tachiyomi/extension/en/warforrayuba/dto/RoundDto.kt
new file mode 100644
index 000000000..8256ba15c
--- /dev/null
+++ b/src/en/warforrayuba/src/eu/kanade/tachiyomi/extension/en/warforrayuba/dto/RoundDto.kt
@@ -0,0 +1,13 @@
+package eu.kanade.tachiyomi.extension.en.warforrayuba.dto
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class RoundDto(
+ val title: String,
+ val description: String,
+ val artist: String,
+ val author: String,
+ val cover: String,
+ val chapters: Map
+)