Myreadingmanga - add more languages (#3088)
* working * integration * cleanup, finalization
This commit is contained in:
		
							parent
							
								
									99ee039d09
								
							
						
					
					
						commit
						a6c2ed3aae
					
				@ -5,7 +5,7 @@ ext {
 | 
			
		||||
    appName = 'Tachiyomi: MyReadingManga'
 | 
			
		||||
    pkgNameSuffix = 'all.myreadingmanga'
 | 
			
		||||
    extClass = '.MyReadingMangaFactory'
 | 
			
		||||
    extVersionCode = 33
 | 
			
		||||
    extVersionCode = 34
 | 
			
		||||
    libVersion = '1.2'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
package eu.kanade.tachiyomi.extension.all.myreadingmanga
 | 
			
		||||
 | 
			
		||||
import android.annotation.SuppressLint
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import eu.kanade.tachiyomi.network.GET
 | 
			
		||||
import eu.kanade.tachiyomi.network.asObservableSuccess
 | 
			
		||||
@ -22,11 +23,11 @@ import org.jsoup.nodes.Document
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import rx.Observable
 | 
			
		||||
 | 
			
		||||
open class MyReadingManga(override val lang: String) : ParsedHttpSource() {
 | 
			
		||||
open class MyReadingManga(override val lang: String, private val siteLang: String, private val latestLang: String) : ParsedHttpSource() {
 | 
			
		||||
 | 
			
		||||
    // Basic Info
 | 
			
		||||
    override val name = "MyReadingManga"
 | 
			
		||||
    override val baseUrl = "https://myreadingmanga.info"
 | 
			
		||||
    final override val baseUrl = "https://myreadingmanga.info"
 | 
			
		||||
    override val client: OkHttpClient = network.cloudflareClient.newBuilder()
 | 
			
		||||
        .connectTimeout(1, TimeUnit.MINUTES)
 | 
			
		||||
        .readTimeout(1, TimeUnit.MINUTES)
 | 
			
		||||
@ -37,106 +38,57 @@ open class MyReadingManga(override val lang: String) : ParsedHttpSource() {
 | 
			
		||||
 | 
			
		||||
    // Popular - Random
 | 
			
		||||
    override fun popularMangaRequest(page: Int): Request {
 | 
			
		||||
        return GET("$baseUrl/search/?wpsolr_sort=sort_by_random&wpsolr_page=$page", headers) // Random Manga as returned by search
 | 
			
		||||
        return GET("$baseUrl/search/?wpsolr_sort=sort_by_random&wpsolr_page=$page&wpsolr_fq[0]=lang_str:$siteLang", headers) // Random Manga as returned by search
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaNextPageSelector() = searchMangaNextPageSelector()
 | 
			
		||||
    override fun popularMangaSelector() = searchMangaSelector()
 | 
			
		||||
    override fun popularMangaParse(response: Response) = searchMangaParse(response)
 | 
			
		||||
    override fun popularMangaFromElement(element: Element) = searchMangaFromElement(element)
 | 
			
		||||
    override fun popularMangaParse(response: Response): MangasPage {
 | 
			
		||||
        if (!filtersCached) {
 | 
			
		||||
            cachedPagesUrls.onEach { filterAssist(it.value) }
 | 
			
		||||
            filtersCached = true
 | 
			
		||||
        }
 | 
			
		||||
        return searchMangaParse(response)
 | 
			
		||||
    }
 | 
			
		||||
    override fun popularMangaNextPageSelector() = throw Exception("Not used")
 | 
			
		||||
    override fun popularMangaSelector() = throw Exception("Not used")
 | 
			
		||||
    override fun popularMangaFromElement(element: Element) = throw Exception("Not used")
 | 
			
		||||
 | 
			
		||||
    // Latest
 | 
			
		||||
    @SuppressLint("DefaultLocale")
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int): Request {
 | 
			
		||||
        return GET("$baseUrl/page/$page/", headers) // Home Page - Latest Manga
 | 
			
		||||
        return GET("$baseUrl/lang/${latestLang.toLowerCase()}" + if (page > 1) "/page/$page/" else "", headers) // Home Page - Latest Manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesNextPageSelector() = "li.pagination-next"
 | 
			
		||||
    override fun latestUpdatesSelector() = "article"
 | 
			
		||||
    override fun latestUpdatesParse(response: Response): MangasPage {
 | 
			
		||||
        val document = response.asJsoup()
 | 
			
		||||
        val mangas = mutableListOf<SManga>()
 | 
			
		||||
        val list = document.select(latestUpdatesSelector()).filter { element ->
 | 
			
		||||
            val select = element.select("a[rel=bookmark]")
 | 
			
		||||
            select.text().contains("[$lang", true)
 | 
			
		||||
        }
 | 
			
		||||
        for (element in list) {
 | 
			
		||||
            mangas.add(latestUpdatesFromElement(element))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val hasNextPage = latestUpdatesNextPageSelector().let { selector ->
 | 
			
		||||
            document.select(selector).first()
 | 
			
		||||
        } != null
 | 
			
		||||
 | 
			
		||||
        return MangasPage(mangas, hasNextPage)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element) = buildManga(element.select("a[rel]").first(), element.select("a.entry-image-link img").first())
 | 
			
		||||
 | 
			
		||||
    // Search
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
 | 
			
		||||
        val uri = if (query.isNotBlank()) {
 | 
			
		||||
            Uri.parse("$baseUrl/search/").buildUpon()
 | 
			
		||||
                .appendQueryParameter("search", query)
 | 
			
		||||
                .appendQueryParameter("wpsolr_page", page.toString())
 | 
			
		||||
        } else {
 | 
			
		||||
            val uri = Uri.parse("$baseUrl/").buildUpon()
 | 
			
		||||
            // Append uri filters
 | 
			
		||||
            filters.forEach {
 | 
			
		||||
                if (it is UriFilter)
 | 
			
		||||
                    it.addToUri(uri)
 | 
			
		||||
        var fqIndex = 0
 | 
			
		||||
        val uri = Uri.parse("$baseUrl/search/").buildUpon()
 | 
			
		||||
            .appendQueryParameter("wpsolr_q", query)
 | 
			
		||||
            .appendQueryParameter("wpsolr_fq[$fqIndex]", "lang_str:$siteLang")
 | 
			
		||||
            .appendQueryParameter("wpsolr_page", page.toString())
 | 
			
		||||
        filters.forEach {
 | 
			
		||||
            if (it is UriFilter) {
 | 
			
		||||
                fqIndex++
 | 
			
		||||
                it.addToUri(uri, "wpsolr_fq[$fqIndex]")
 | 
			
		||||
            }
 | 
			
		||||
            uri.appendPath("page").appendPath("$page")
 | 
			
		||||
        }
 | 
			
		||||
        return GET(uri.toString(), headers)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaNextPageSelector(): String? = null
 | 
			
		||||
    override fun searchMangaNextPageSelector(): String? = throw Exception("Not used")
 | 
			
		||||
    override fun searchMangaSelector() = "div.results-by-facets div[id*=res]"
 | 
			
		||||
    private var mangaParsedSoFar = 0
 | 
			
		||||
    override fun searchMangaParse(response: Response): MangasPage {
 | 
			
		||||
        // Filter Assist - Caches Pages required for filter parsing
 | 
			
		||||
        if (!filtersCached) {
 | 
			
		||||
            filterAssist(baseUrl)
 | 
			
		||||
            filterAssist("$baseUrl/cats/")
 | 
			
		||||
            filterAssist("$baseUrl/pairing/")
 | 
			
		||||
            filterAssist("$baseUrl/group/")
 | 
			
		||||
            filtersCached = true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val document = response.asJsoup()
 | 
			
		||||
        val mangas = mutableListOf<SManga>()
 | 
			
		||||
        // Process Search Results
 | 
			
		||||
        if (document.baseUri().contains("search", true)) {
 | 
			
		||||
            val elements = document.select(searchMangaSelector())
 | 
			
		||||
            for (element in elements) {
 | 
			
		||||
                if (element.text().contains("[$lang", true)) {
 | 
			
		||||
                    mangas.add(searchMangaFromElement(element))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            val hasNextPage = searchMangaSelector().let { selector ->
 | 
			
		||||
                document.select(selector).first()
 | 
			
		||||
            } != null
 | 
			
		||||
 | 
			
		||||
            return MangasPage(mangas, hasNextPage)
 | 
			
		||||
        }
 | 
			
		||||
        // Process Filter Results / Same theme as home page
 | 
			
		||||
        else {
 | 
			
		||||
            // return popularMangaParse(response)
 | 
			
		||||
            val list = document.select(latestUpdatesSelector()).filter { element ->
 | 
			
		||||
                val select = element.select("a[rel=bookmark]")
 | 
			
		||||
                select.text().contains("[$lang", true)
 | 
			
		||||
            }
 | 
			
		||||
            for (element in list) {
 | 
			
		||||
                mangas.add(latestUpdatesFromElement(element))
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val hasNextPage = latestUpdatesNextPageSelector().let { selector ->
 | 
			
		||||
                document.select(selector).first()
 | 
			
		||||
            } != null
 | 
			
		||||
 | 
			
		||||
            return MangasPage(mangas, hasNextPage)
 | 
			
		||||
        }
 | 
			
		||||
        if (document.location().contains("page=1")) mangaParsedSoFar = 0
 | 
			
		||||
        val mangas = document.select(searchMangaSelector()).map { searchMangaFromElement(it) }
 | 
			
		||||
            .also { mangaParsedSoFar += it.count() }
 | 
			
		||||
        val totalResults = Regex("""(\d+)""").find(document.select("div.res_info").text())?.groupValues?.get(1)?.toIntOrNull() ?: 0
 | 
			
		||||
        return MangasPage(mangas, mangaParsedSoFar < totalResults)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaFromElement(element: Element) = buildManga(element.select("a").first(), element.select("img")?.first())
 | 
			
		||||
 | 
			
		||||
    // Build Manga From Element
 | 
			
		||||
@ -184,8 +136,7 @@ open class MyReadingManga(override val lang: String) : ParsedHttpSource() {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.author = cleanAuthor(document.select("h1").text())
 | 
			
		||||
        manga.artist = cleanAuthor(document.select("h1").text())
 | 
			
		||||
        val glist = document.select(".entry-header p a[href*=genre]").map { it.text() }
 | 
			
		||||
        manga.genre = glist.joinToString(", ")
 | 
			
		||||
        manga.genre = document.select(".entry-header p a[href*=genre]").joinToString(", ") { it.text() }
 | 
			
		||||
        val extendedDescription = document.select(".entry-content p:not(p:containsOwn(|)):not(.chapter-class + p)")?.joinToString("\n") { it.text() }
 | 
			
		||||
        manga.description = document.select("h1").text() + if (extendedDescription.isNullOrEmpty()) "" else "\n\n$extendedDescription"
 | 
			
		||||
        manga.status = when (document.select("a[href*=status]")?.first()?.text()) {
 | 
			
		||||
@ -207,6 +158,7 @@ open class MyReadingManga(override val lang: String) : ParsedHttpSource() {
 | 
			
		||||
    // Start Chapter Get
 | 
			
		||||
    override fun chapterListSelector() = ".entry-pagination a"
 | 
			
		||||
 | 
			
		||||
    @SuppressLint("DefaultLocale")
 | 
			
		||||
    override fun chapterListParse(response: Response): List<SChapter> {
 | 
			
		||||
        val document = response.asJsoup()
 | 
			
		||||
        val chapters = mutableListOf<SChapter>()
 | 
			
		||||
@ -285,63 +237,53 @@ open class MyReadingManga(override val lang: String) : ParsedHttpSource() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Parses page for filter
 | 
			
		||||
    private fun returnFilter(document: Document?, css: String, attributekey: String): Array<Pair<String, String>> {
 | 
			
		||||
        return document?.select(css)?.map { Pair(it.attr(attributekey).substringBeforeLast("/").substringAfterLast("/"), it.text()) }?.toTypedArray()
 | 
			
		||||
            ?: arrayOf(Pair("", "Press 'Reset' to try again"))
 | 
			
		||||
    private fun returnFilter(document: Document?, css: String): Array<String> {
 | 
			
		||||
        return document?.select(css)?.map { it.text() }?.toTypedArray()
 | 
			
		||||
            ?: arrayOf("Press 'Reset' to try again")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // URLs for the pages we need to cache
 | 
			
		||||
    private val cachedPagesUrls = hashMapOf(
 | 
			
		||||
        Pair("genres", baseUrl),
 | 
			
		||||
        Pair("tags", baseUrl),
 | 
			
		||||
        Pair("categories", "$baseUrl/cats/"),
 | 
			
		||||
        Pair("pairings", "$baseUrl/pairing/"),
 | 
			
		||||
        Pair("groups", "$baseUrl/group/")
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    // Generates the filter lists for app
 | 
			
		||||
    override fun getFilterList(): FilterList {
 | 
			
		||||
        return FilterList(
 | 
			
		||||
            // MRM does not support genre filtering and text search at the same time
 | 
			
		||||
            Filter.Header("NOTE: Filters are ignored if using text search."),
 | 
			
		||||
            Filter.Header("Only one filter can be used at a time."),
 | 
			
		||||
            GenreFilter(returnFilter(getCache(baseUrl), ".tagcloud a[href*=/genre/]", "href")),
 | 
			
		||||
            TagFilter(returnFilter(getCache(baseUrl), ".tagcloud a[href*=/tag/]", "href")),
 | 
			
		||||
            CatFilter(returnFilter(getCache("$baseUrl/cats/"), ".links a", "abs:href")),
 | 
			
		||||
            PairingFilter(returnFilter(getCache("$baseUrl/pairing/"), ".links a", "abs:href")),
 | 
			
		||||
            ScanGroupFilter(returnFilter(getCache("$baseUrl/group/"), ".links a", "abs:href"))
 | 
			
		||||
            GenreFilter(returnFilter(getCache(cachedPagesUrls["genres"]!!), ".tagcloud a[href*=/genre/]")),
 | 
			
		||||
            TagFilter(returnFilter(getCache(cachedPagesUrls["tags"]!!), ".tagcloud a[href*=/tag/]")),
 | 
			
		||||
            CatFilter(returnFilter(getCache(cachedPagesUrls["categories"]!!), ".links a")),
 | 
			
		||||
            PairingFilter(returnFilter(getCache(cachedPagesUrls["pairings"]!!), ".links a")),
 | 
			
		||||
            ScanGroupFilter(returnFilter(getCache(cachedPagesUrls["groups"]!!), ".links a"))
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class GenreFilter(GENRES: Array<Pair<String, String>>) : UriSelectFilterPath("Genre", "genre", arrayOf(Pair("", "Any"), *GENRES))
 | 
			
		||||
    private class TagFilter(POPTAG: Array<Pair<String, String>>) : UriSelectFilterPath("Popular Tags", "tag", arrayOf(Pair("", "Any"), *POPTAG))
 | 
			
		||||
    private class CatFilter(CATID: Array<Pair<String, String>>) : UriSelectFilterShortPath("Categories", "cat", arrayOf(Pair("", "Any"), *CATID))
 | 
			
		||||
    private class PairingFilter(PAIR: Array<Pair<String, String>>) : UriSelectFilterPath("Pairing", "pairing", arrayOf(Pair("", "Any"), *PAIR))
 | 
			
		||||
    private class ScanGroupFilter(GROUP: Array<Pair<String, String>>) : UriSelectFilterPath("Scanlation Group", "group", arrayOf(Pair("", "Any"), *GROUP))
 | 
			
		||||
    private class GenreFilter(GENRES: Array<String>) : UriSelectFilter("Genre", "genre_str", arrayOf("Any", *GENRES))
 | 
			
		||||
    private class TagFilter(POPTAG: Array<String>) : UriSelectFilter("Popular Tags", "tags", arrayOf("Any", *POPTAG))
 | 
			
		||||
    private class CatFilter(CATID: Array<String>) : UriSelectFilter("Categories", "categories", arrayOf("Any", *CATID))
 | 
			
		||||
    private class PairingFilter(PAIR: Array<String>) : UriSelectFilter("Pairing", "pairing_str", arrayOf("Any", *PAIR))
 | 
			
		||||
    private class ScanGroupFilter(GROUP: Array<String>) : UriSelectFilter("Scanlation Group", "group_str", arrayOf("Any", *GROUP))
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Class that creates a select filter. Each entry in the dropdown has a name and a display name.
 | 
			
		||||
     * If an entry is selected it is appended as a query parameter onto the end of the URI.
 | 
			
		||||
     * If `firstIsUnspecified` is set to true, if the first entry is selected, nothing will be appended on the the URI.
 | 
			
		||||
     */
 | 
			
		||||
    // vals: <name, display>
 | 
			
		||||
    private open class UriSelectFilterPath(
 | 
			
		||||
    private open class UriSelectFilter(
 | 
			
		||||
        displayName: String,
 | 
			
		||||
        val uriParam: String,
 | 
			
		||||
        val vals: Array<Pair<String, String>>,
 | 
			
		||||
        val uriValuePrefix: String,
 | 
			
		||||
        val vals: Array<String>,
 | 
			
		||||
        val firstIsUnspecified: Boolean = true,
 | 
			
		||||
        defaultValue: Int = 0
 | 
			
		||||
    ) :
 | 
			
		||||
        Filter.Select<String>(displayName, vals.map { it.second }.toTypedArray(), defaultValue), UriFilter {
 | 
			
		||||
        override fun addToUri(uri: Uri.Builder) {
 | 
			
		||||
        Filter.Select<String>(displayName, vals.map { it }.toTypedArray(), defaultValue), UriFilter {
 | 
			
		||||
        override fun addToUri(uri: Uri.Builder, uriParam: String) {
 | 
			
		||||
            if (state != 0 || !firstIsUnspecified)
 | 
			
		||||
                uri.appendPath(uriParam)
 | 
			
		||||
                    .appendPath(vals[state].first)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private open class UriSelectFilterShortPath(
 | 
			
		||||
        displayName: String,
 | 
			
		||||
        val uriParam: String,
 | 
			
		||||
        val vals: Array<Pair<String, String>>,
 | 
			
		||||
        val firstIsUnspecified: Boolean = true,
 | 
			
		||||
        defaultValue: Int = 0
 | 
			
		||||
    ) :
 | 
			
		||||
        Filter.Select<String>(displayName, vals.map { it.second }.toTypedArray(), defaultValue), UriFilter {
 | 
			
		||||
        override fun addToUri(uri: Uri.Builder) {
 | 
			
		||||
            if (state != 0 || !firstIsUnspecified)
 | 
			
		||||
                uri.appendPath(vals[state].first)
 | 
			
		||||
                uri.appendQueryParameter(uriParam, "$uriValuePrefix:${vals[state]}")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -349,6 +291,6 @@ open class MyReadingManga(override val lang: String) : ParsedHttpSource() {
 | 
			
		||||
     * Represents a filter that is able to modify a URI.
 | 
			
		||||
     */
 | 
			
		||||
    private interface UriFilter {
 | 
			
		||||
        fun addToUri(uri: Uri.Builder)
 | 
			
		||||
        fun addToUri(uri: Uri.Builder, uriParam: String)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,9 +3,43 @@ package eu.kanade.tachiyomi.extension.all.myreadingmanga
 | 
			
		||||
import eu.kanade.tachiyomi.source.Source
 | 
			
		||||
import eu.kanade.tachiyomi.source.SourceFactory
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
class MyReadingMangaFactory : SourceFactory {
 | 
			
		||||
    override fun createSources(): List<Source> = getAllMyReadingMangaLanguages()
 | 
			
		||||
    override fun createSources(): List<Source> = languageList.map { MyReadingManga(it.tachiLang, it.siteLang, it.latestLang) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
private data class Source(val tachiLang: String, val siteLang: String, val latestLang: String = siteLang)
 | 
			
		||||
 | 
			
		||||
// These should all be valid. Add a language code and uncomment to enable
 | 
			
		||||
private val languageList = listOf(
 | 
			
		||||
    Source("ar", "Arabic"),
 | 
			
		||||
//    Source("", "Bahasa"),
 | 
			
		||||
    Source("id", "Indonesia"),
 | 
			
		||||
//    Source("", "Bulgarian"),
 | 
			
		||||
    Source("zh", "Chinese"),
 | 
			
		||||
//    Source("", "Croatian"),
 | 
			
		||||
//    Source("", "Czech"),
 | 
			
		||||
    Source("en", "English"),
 | 
			
		||||
//    Source("", "Filipino"),
 | 
			
		||||
//    Source("", "Finnish"),
 | 
			
		||||
//    Source("", "Flemish", "flemish-dutch"),
 | 
			
		||||
//    Source("", "Dutch"),
 | 
			
		||||
    Source("fr", "French"),
 | 
			
		||||
    Source("de", "German"),
 | 
			
		||||
//    Source("", "Greek"),
 | 
			
		||||
//    Source("", "Hebrew"),
 | 
			
		||||
//    Source("", "Hindi"),
 | 
			
		||||
//    Source("", "Hungarian"),
 | 
			
		||||
    Source("it", "Italian"),
 | 
			
		||||
    Source("ja", "Japanese", "jp"),
 | 
			
		||||
    Source("ko", "Korean"),
 | 
			
		||||
//    Source("", "Polish"),
 | 
			
		||||
    Source("pt-BR", "Portuguese"),
 | 
			
		||||
//    Source("", "Romanian"),
 | 
			
		||||
    Source("ru", "Russian"),
 | 
			
		||||
//    Source("", "Slovak"),
 | 
			
		||||
    Source("es", "Spanish"),
 | 
			
		||||
//    Source("", "Swedish"),
 | 
			
		||||
//    Source("", "Thai"),
 | 
			
		||||
    Source("tr", "Turkish"),
 | 
			
		||||
    Source("vi", "Vietnamese")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -1,11 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.extension.all.myreadingmanga
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * MyReadingManga languages
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
class MyReadingMangaEnglish : MyReadingManga("en")
 | 
			
		||||
 | 
			
		||||
fun getAllMyReadingMangaLanguages() = listOf(
 | 
			
		||||
        MyReadingMangaEnglish()
 | 
			
		||||
)
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user