feat: 去除枝网查重
This commit is contained in:
parent
d3ddf23988
commit
6eef2fcf06
|
|
@ -7,14 +7,6 @@
|
||||||
"author": "huanchengfly",
|
"author": "huanchengfly",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"main_class": "com.huanchengfly.tieba.post.plugins.PluginCommentLookup"
|
"main_class": "com.huanchengfly.tieba.post.plugins.PluginCommentLookup"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "AsoulCnki",
|
|
||||||
"name": "枝网查重",
|
|
||||||
"desc": "调用枝网查重 API 查询重复小作文",
|
|
||||||
"author": "huanchengfly",
|
|
||||||
"version": "1.0",
|
|
||||||
"main_class": "com.huanchengfly.tieba.post.plugins.asoulcnki.PluginAsoulCnki"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
package com.huanchengfly.tieba.post.plugins.asoulcnki;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.text.TextPaint;
|
|
||||||
import android.text.style.ClickableSpan;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.huanchengfly.tieba.post.R;
|
|
||||||
import com.huanchengfly.tieba.post.ui.common.theme.utils.ThemeUtils;
|
|
||||||
import com.huanchengfly.tieba.post.utils.UtilsKt;
|
|
||||||
|
|
||||||
public class MyURLSpan extends ClickableSpan {
|
|
||||||
public String url;
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
public MyURLSpan(Context context, String url) {
|
|
||||||
super();
|
|
||||||
this.url = url;
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateDrawState(@NonNull TextPaint ds) {
|
|
||||||
super.updateDrawState(ds);
|
|
||||||
ds.setColor(ThemeUtils.getColorByAttr(this.context, R.attr.colorAccent));
|
|
||||||
ds.setUnderlineText(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(@NonNull View view) {
|
|
||||||
UtilsKt.launchUrl(context, url);
|
|
||||||
//context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,176 +0,0 @@
|
||||||
package com.huanchengfly.tieba.post.plugins.asoulcnki
|
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.text.SpannableStringBuilder
|
|
||||||
import android.text.Spanned
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.ProgressBar
|
|
||||||
import android.widget.TextView
|
|
||||||
import com.huanchengfly.tieba.post.R
|
|
||||||
import com.huanchengfly.tieba.post.api.models.ThreadContentBean
|
|
||||||
import com.huanchengfly.tieba.post.components.LinkTouchMovementMethod
|
|
||||||
import com.huanchengfly.tieba.post.components.spans.MyImageSpan
|
|
||||||
import com.huanchengfly.tieba.post.plugins.IPlugin
|
|
||||||
import com.huanchengfly.tieba.post.plugins.asoulcnki.api.CheckApi
|
|
||||||
import com.huanchengfly.tieba.post.plugins.asoulcnki.models.CheckApiBody
|
|
||||||
import com.huanchengfly.tieba.post.plugins.interfaces.IApp
|
|
||||||
import com.huanchengfly.tieba.post.plugins.models.PluginManifest
|
|
||||||
import com.huanchengfly.tieba.post.plugins.registerMenuItem
|
|
||||||
import com.huanchengfly.tieba.post.toJson
|
|
||||||
import com.huanchengfly.tieba.post.ui.common.theme.utils.ThemeUtils
|
|
||||||
import com.huanchengfly.tieba.post.utils.DisplayUtil
|
|
||||||
import com.huanchengfly.tieba.post.utils.Util
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
|
||||||
import java.text.NumberFormat
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Date
|
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
class PluginAsoulCnki(app: IApp, manifest: PluginManifest) : IPlugin(app, manifest) {
|
|
||||||
override fun onEnable() {
|
|
||||||
super.onEnable()
|
|
||||||
registerMenuItem<ThreadContentBean.PostListItemBean>(
|
|
||||||
"asoul_cnki_check",
|
|
||||||
context.getString(R.string.plugin_asoul_cnki_check)
|
|
||||||
) { data ->
|
|
||||||
val dialog = app.showLoadingDialog()
|
|
||||||
val body = CheckApiBody(getPostTextContent(data)).toJson()
|
|
||||||
launch(Dispatchers.IO + job) {
|
|
||||||
val result =
|
|
||||||
CheckApi.instance.checkAsync(body.toRequestBody("application/json, charset=utf-8".toMediaTypeOrNull()))
|
|
||||||
.await()
|
|
||||||
launch(Dispatchers.Main + job) {
|
|
||||||
dialog.cancel()
|
|
||||||
if (result.code == 0) {
|
|
||||||
val numberFormatter = NumberFormat.getNumberInstance().apply {
|
|
||||||
maximumFractionDigits = 2
|
|
||||||
minimumFractionDigits = 2
|
|
||||||
}
|
|
||||||
app.showAlertDialog {
|
|
||||||
setTitle("查重结果")
|
|
||||||
val percent = "${numberFormatter.format(result.data.rate * 100.0)}%"
|
|
||||||
val resultForCopy = context.getString(
|
|
||||||
R.string.plugin_asoul_cnki_result,
|
|
||||||
formatDateTime("yyyy-MM-dd HH:mm:ss"),
|
|
||||||
percent,
|
|
||||||
if (result.data.related.isNotEmpty()) {
|
|
||||||
context.getString(
|
|
||||||
R.string.plugin_asoul_cnki_related,
|
|
||||||
result.data.related[0].replyUrl,
|
|
||||||
result.data.related[0].reply.mName,
|
|
||||||
formatDateTime(
|
|
||||||
"yyyy-MM-dd HH:mm",
|
|
||||||
result.data.related[0].reply.ctime * 1000L
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
)
|
|
||||||
val view = View.inflate(
|
|
||||||
context,
|
|
||||||
R.layout.plugin_asoul_cnki_dialog_check_result,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
val percentView = view.findViewById<TextView>(R.id.check_result_percent)
|
|
||||||
val progress =
|
|
||||||
view.findViewById<ProgressBar>(R.id.check_result_progress)
|
|
||||||
val relatedView = view.findViewById<View>(R.id.check_result_related)
|
|
||||||
val relatedTitle =
|
|
||||||
view.findViewById<TextView>(R.id.check_result_related_title)
|
|
||||||
val relatedContent =
|
|
||||||
view.findViewById<TextView>(R.id.check_result_related_content)
|
|
||||||
percentView.text = context.getString(
|
|
||||||
R.string.plugin_asoul_cnki_check_result_percent,
|
|
||||||
percent
|
|
||||||
)
|
|
||||||
progress.progress = (result.data.rate * 10000).toInt()
|
|
||||||
if (result.data.related.isNullOrEmpty()) {
|
|
||||||
relatedView.visibility = View.GONE
|
|
||||||
} else {
|
|
||||||
relatedView.visibility = View.VISIBLE
|
|
||||||
relatedTitle.text = context.getString(
|
|
||||||
R.string.plugin_asoul_cnki_check_result_related,
|
|
||||||
result.data.related.size
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val relatedContentText = SpannableStringBuilder()
|
|
||||||
result.data.related.forEach {
|
|
||||||
relatedContentText.appendLink("${it.reply.mName} 的评论", it.replyUrl)
|
|
||||||
.append("\n")
|
|
||||||
}
|
|
||||||
relatedContent.apply {
|
|
||||||
text = relatedContentText
|
|
||||||
movementMethod = LinkTouchMovementMethod.getInstance()
|
|
||||||
}
|
|
||||||
setView(view)
|
|
||||||
setPositiveButton(R.string.btn_copy_check_result) { _, _ ->
|
|
||||||
app.copyText(resultForCopy)
|
|
||||||
}
|
|
||||||
setNegativeButton(R.string.btn_close, null)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
app.toastShort("查重失败 ${result.code}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SpannableStringBuilder.appendLink(
|
|
||||||
text: CharSequence,
|
|
||||||
url: String
|
|
||||||
): SpannableStringBuilder {
|
|
||||||
val spannableStringBuilder = SpannableStringBuilder()
|
|
||||||
val size = DisplayUtil.sp2px(context, 14f)
|
|
||||||
val bitmap = Util.tintBitmap(
|
|
||||||
Bitmap.createScaledBitmap(
|
|
||||||
Util.getBitmapFromVectorDrawable(
|
|
||||||
context,
|
|
||||||
R.drawable.ic_link
|
|
||||||
),
|
|
||||||
size,
|
|
||||||
size,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
ThemeUtils.getColorByAttr(context, R.attr.colorAccent)
|
|
||||||
)
|
|
||||||
spannableStringBuilder.append(
|
|
||||||
"[链接]",
|
|
||||||
MyImageSpan(context, bitmap),
|
|
||||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
||||||
)
|
|
||||||
spannableStringBuilder.append(" ")
|
|
||||||
spannableStringBuilder.append(
|
|
||||||
text,
|
|
||||||
MyURLSpan(context, url),
|
|
||||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
||||||
)
|
|
||||||
return append(spannableStringBuilder)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getPostTextContent(item: ThreadContentBean.PostListItemBean): String {
|
|
||||||
val stringBuilder = StringBuilder()
|
|
||||||
for (contentBean in item.content ?: emptyList()) {
|
|
||||||
when (contentBean.type) {
|
|
||||||
"2" -> contentBean.setText("#(" + contentBean.c + ")")
|
|
||||||
"3", "20" -> contentBean.setText("[图片]\n")
|
|
||||||
"10" -> contentBean.setText("[语音]\n")
|
|
||||||
}
|
|
||||||
if (contentBean.text != null) {
|
|
||||||
stringBuilder.append(contentBean.text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return stringBuilder.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun formatDateTime(
|
|
||||||
pattern: String,
|
|
||||||
timestamp: Long = System.currentTimeMillis()
|
|
||||||
): String {
|
|
||||||
return SimpleDateFormat(pattern, Locale.getDefault()).format(Date(timestamp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
package com.huanchengfly.tieba.post.plugins.asoulcnki.api
|
|
||||||
|
|
||||||
import com.huanchengfly.tieba.post.api.retrofit.NullOnEmptyConverterFactory
|
|
||||||
import com.huanchengfly.tieba.post.api.retrofit.adapter.DeferredCallAdapterFactory
|
|
||||||
import com.huanchengfly.tieba.post.api.retrofit.converter.gson.GsonConverterFactory
|
|
||||||
import okhttp3.ConnectionPool
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import retrofit2.Retrofit
|
|
||||||
|
|
||||||
object CheckApi {
|
|
||||||
private val connectionPool = ConnectionPool()
|
|
||||||
|
|
||||||
val instance: ICheckApi by lazy {
|
|
||||||
Retrofit.Builder()
|
|
||||||
.baseUrl("https://asoulcnki.asia/")
|
|
||||||
.addCallAdapterFactory(DeferredCallAdapterFactory())
|
|
||||||
.addConverterFactory(NullOnEmptyConverterFactory())
|
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
|
||||||
.client(OkHttpClient.Builder().apply {
|
|
||||||
connectionPool(connectionPool)
|
|
||||||
sslSocketFactory(
|
|
||||||
SSLSocketClient.getSSLSocketFactory(),
|
|
||||||
SSLSocketClient.getX509TrustManager()
|
|
||||||
)
|
|
||||||
hostnameVerifier(SSLSocketClient.getHostnameVerifier())
|
|
||||||
}.build())
|
|
||||||
.build()
|
|
||||||
.create(ICheckApi::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
package com.huanchengfly.tieba.post.plugins.asoulcnki.api
|
|
||||||
|
|
||||||
import com.huanchengfly.tieba.post.api.ParamExpression
|
|
||||||
import com.huanchengfly.tieba.post.api.forEachNonNull
|
|
||||||
import okhttp3.Interceptor
|
|
||||||
import okhttp3.Response
|
|
||||||
|
|
||||||
class CommonHeaderInterceptor(private vararg val additionHeaders: ParamExpression) : Interceptor {
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
|
||||||
val request = chain.request()
|
|
||||||
val headers = request.headers
|
|
||||||
|
|
||||||
return chain.proceed(request.newBuilder().apply {
|
|
||||||
additionHeaders.forEachNonNull { name, value ->
|
|
||||||
if (headers[name] == null) addHeader(name, value)
|
|
||||||
}
|
|
||||||
}.build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
package com.huanchengfly.tieba.post.plugins.asoulcnki.api
|
|
||||||
|
|
||||||
import com.huanchengfly.tieba.post.plugins.asoulcnki.models.CheckResult
|
|
||||||
import kotlinx.coroutines.Deferred
|
|
||||||
import okhttp3.RequestBody
|
|
||||||
import retrofit2.http.Body
|
|
||||||
import retrofit2.http.Headers
|
|
||||||
import retrofit2.http.POST
|
|
||||||
|
|
||||||
interface ICheckApi {
|
|
||||||
@POST("/v1/api/check")
|
|
||||||
@Headers("content-type: application/json;charset=UTF-8")
|
|
||||||
fun checkAsync(
|
|
||||||
@Body requestBody: RequestBody
|
|
||||||
): Deferred<CheckResult>
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
package com.huanchengfly.tieba.post.plugins.asoulcnki.api;
|
|
||||||
|
|
||||||
import java.security.KeyStore;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import javax.net.ssl.HostnameVerifier;
|
|
||||||
import javax.net.ssl.SSLContext;
|
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
|
||||||
import javax.net.ssl.TrustManager;
|
|
||||||
import javax.net.ssl.TrustManagerFactory;
|
|
||||||
import javax.net.ssl.X509TrustManager;
|
|
||||||
|
|
||||||
|
|
||||||
public class SSLSocketClient {
|
|
||||||
|
|
||||||
//获取这个SSLSocketFactory
|
|
||||||
public static SSLSocketFactory getSSLSocketFactory() {
|
|
||||||
try {
|
|
||||||
SSLContext sslContext = SSLContext.getInstance("SSL");
|
|
||||||
sslContext.init(null, getTrustManager(), new SecureRandom());
|
|
||||||
return sslContext.getSocketFactory();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//获取TrustManager
|
|
||||||
private static TrustManager[] getTrustManager() {
|
|
||||||
return new TrustManager[]{
|
|
||||||
new X509TrustManager() {
|
|
||||||
@Override
|
|
||||||
public void checkClientTrusted(X509Certificate[] chain, String authType) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void checkServerTrusted(X509Certificate[] chain, String authType) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public X509Certificate[] getAcceptedIssuers() {
|
|
||||||
return new X509Certificate[]{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//获取HostnameVerifier
|
|
||||||
public static HostnameVerifier getHostnameVerifier() {
|
|
||||||
return (s, sslSession) -> true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static X509TrustManager getX509TrustManager() {
|
|
||||||
X509TrustManager trustManager = null;
|
|
||||||
try {
|
|
||||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
|
||||||
trustManagerFactory.init((KeyStore) null);
|
|
||||||
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
|
|
||||||
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
|
|
||||||
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
|
|
||||||
}
|
|
||||||
trustManager = (X509TrustManager) trustManagers[0];
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return trustManager;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
package com.huanchengfly.tieba.post.plugins.asoulcnki.models
|
|
||||||
|
|
||||||
data class CheckApiBody(
|
|
||||||
val text: String
|
|
||||||
)
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
package com.huanchengfly.tieba.post.plugins.asoulcnki.models
|
|
||||||
|
|
||||||
import androidx.annotation.Keep
|
|
||||||
import com.google.gson.annotations.SerializedName
|
|
||||||
|
|
||||||
@Keep
|
|
||||||
data class CheckResult(
|
|
||||||
val code: Int, // 0
|
|
||||||
val `data`: Data,
|
|
||||||
val message: String // success
|
|
||||||
) {
|
|
||||||
@Keep
|
|
||||||
data class Data(
|
|
||||||
@SerializedName("end_time")
|
|
||||||
val endTime: Int, // 1629010807
|
|
||||||
val rate: Double, // 1.0
|
|
||||||
val related: List<Related>,
|
|
||||||
@SerializedName("start_time")
|
|
||||||
val startTime: Int // 1606137506
|
|
||||||
) {
|
|
||||||
@Keep
|
|
||||||
data class Related(
|
|
||||||
val rate: Double, // 1.0
|
|
||||||
val reply: Reply,
|
|
||||||
@SerializedName("reply_url")
|
|
||||||
val replyUrl: String // https://www.bilibili.com/video/av377092608/#reply5051494613
|
|
||||||
) {
|
|
||||||
@Keep
|
|
||||||
data class Reply(
|
|
||||||
val content: String, // 曾几何时,我也想像asoul的beeeeeeeela一样做幸福滤镜下的事至少在这层滤镜下,beeeeeeeela的一举一动都是随心所欲且浪漫真实的当我看到beeeeeeeela能像个二次元一样和弹幕大谈特谈50音,当我看到beeeeeeeela能够笑着在夜里唱着不知道练了多少遍的云烟成雨,当我看到她可以在失落后得到安抚和拥抱…以往的笑意消散殆尽,剩下的只有我对beeeeeeeela浪漫的感动和一种无中生有的失意了。我也想像她一样。但这是虚假的,每次在烂醉酩酊起来后依然会痛苦,每次在浪费时间的时候都能意识到,你不能感受到我感受到的东西。但就算是这样,没了你我可能就会完蛋了吧。因为我们需要一个梦。
|
|
||||||
val ctime: Int, // 1627881576
|
|
||||||
@SerializedName("dynamic_id")
|
|
||||||
val dynamicId: String, // 553473662133564230
|
|
||||||
@SerializedName("like_num")
|
|
||||||
val likeNum: Int, // 9
|
|
||||||
@SerializedName("m_name")
|
|
||||||
val mName: String, // 走出童年
|
|
||||||
val mid: Int, // 671239951
|
|
||||||
val oid: String, // 377092608
|
|
||||||
@SerializedName("origin_rpid")
|
|
||||||
val originRpid: String, // -1
|
|
||||||
val rpid: String, // 5051494613
|
|
||||||
@SerializedName("similar_count")
|
|
||||||
val similarCount: Int, // 1
|
|
||||||
@SerializedName("similar_like_sum")
|
|
||||||
val similarLikeSum: Int, // 424
|
|
||||||
@SerializedName("type_id")
|
|
||||||
val typeId: Int, // 1
|
|
||||||
val uid: Int // 672346917
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue