feat: 去除枝网查重

This commit is contained in:
HuanCheng65 2023-01-05 14:48:57 +08:00
parent d3ddf23988
commit 6eef2fcf06
No known key found for this signature in database
GPG Key ID: E9031EF91A805148
9 changed files with 0 additions and 413 deletions

View File

@ -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"
} }
] ]
} }

View File

@ -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));
}
}

View File

@ -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))
}
}

View File

@ -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)
}
}

View File

@ -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())
}
}

View File

@ -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>
}

View File

@ -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;
}
}

View File

@ -1,5 +0,0 @@
package com.huanchengfly.tieba.post.plugins.asoulcnki.models
data class CheckApiBody(
val text: String
)

View File

@ -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
)
}
}
}