feat: 加个给我妹用的小工具

This commit is contained in:
539943419 committed 2025-01-15 18:44:45 +08:00
1 parent d7f1bf7d49
commit 24f910e12c
8 files changed
+391 -25

No files matched your search

+2 -2
View File
@@ -2,12 +2,12 @@
<div class="post-editor">
<!-- editor以后再写 -->
<el-row :gutter="10">
<el-col :span="8">
<el-col :span="12">
<el-input v-model:model-value="title" placeholder="title">
<template #prepend>标题</template>
</el-input>
</el-col>
<el-col :span="16">
<el-col :span="12">
<el-input-tag v-model:model-value="tags" placeholder="添加标签" />
</el-col>
</el-row>
+10
View File
@@ -124,6 +124,16 @@ const PageContent = reactive<PageContent[]>([
}
]
},
{
name: '识趣阅读刷赞',
description: '给我妹自嗨用的',
nuxtLinks: [
{
name: 'link',
to: `/tools/fucksq`
}
]
},
]
}, {
subTitle: '学习资料',
+12 -4
View File
@@ -1,6 +1,6 @@
<template>
<div>
<div class="tools-wrapper">
<NuxtPage />
</div>
</template>
@@ -8,6 +8,14 @@
</script>
<style scoped>
<style lang="scss" scoped>
.tools-wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 0 auto;
// max-width: 800px;
padding: 0;
}
</style>
+148
View File
@@ -0,0 +1,148 @@
<template>
<div class="fuck-sq-wrapper">
<h1 style="margin-bottom: -5px;">反正是个刷赞的就对了</h1>
<el-divider></el-divider>
<main class="fuck-sq-main">
<el-row style="width: 100%;" :gutter="20">
<el-col :span="24">
<el-input v-model="url">
<template #prepend>链接</template>
</el-input>
</el-col>
</el-row>
<el-row style="width: 100%;" :gutter="20">
<el-col :span="16" class="col-flex-end">
<el-text size="large">刷赞数:</el-text>
<el-input-number v-model="likes" :min="1" :max="300" />
</el-col>
<el-col :span="4" class="col-flex-end">
<el-button type="primary" style="width: 100%"
:disabled="status === 'busy' || id === undefined" @click="handleStart">开始</el-button>
</el-col>
<el-col :span="4" class="col-flex-end">
<el-button type="primary" style="width: 100%" :disabled="messages.length === 0" @click="handleClear">清空输出</el-button>
</el-col>
</el-row>
<div class="message-container-wrapper">
<el-scrollbar>
<div class="message-container">
<el-text v-for="(message, index) in messages" :key="message.timestamp" class="message-container-message">
<span style="margin-right: 7px;">{{ `${_fd(message.timestamp)} ==>`
}}</span>
<span>{{ message.message }}</span>
</el-text>
</div>
</el-scrollbar>
</div>
</main>
</div>
</template>
<script setup lang="ts">
const url = ref('')
const likes = ref(10)
const status = ref<'waiting' | 'busy'>('waiting')
const messages = reactive<{
timestamp: number,
message: string
}[]>([])
const log = (...message: string[]) => {
messages.push({
timestamp: Date.now(),
message: message.join('')
})
}
const id = computed(() => {
const idMatch0 = url.value.match(/id%3D(\d+)/);
const idMatch1 = url.value.match(/id=(\d+)/);
return idMatch0 ? idMatch0[1] : idMatch1 ? idMatch1[1] : undefined
})
const _fd = (
date: number,
) => formatDate(date, 'yyyy-MM-dd HH:mm:ss')
const handleStart = () => {
const sse = new EventSource(`/api/tools/shiqu/like?id=${id.value}&likes=${likes.value}`);
sse.onopen = function (event) {
log('建立与服务器的连接')
};
sse.onmessage = function (event) {
log('Received message:', event.data);
};
sse.onerror = function (event) {
log('Error occurred:');
console.error(event)
sse.close()
};
}
const handleClear = () => {
messages.splice(0, messages.length)
}
watchEffect(() => {
if (id.value) {
log(`识别到id: ${id.value}`)
} else {
log(`未识别到id`)
}
})
</script>
<style scoped>
.fuck-sq-wrapper {
width: 95%;
height: 100%;
max-width: 800px;
display: flex;
flex-direction: column;
align-items: center;
padding: 10px;
}
.fuck-sq-main {
width: 100%;
height: 100%;
max-width: 800px;
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
}
.col-flex-end {
display: flex;
justify-content: flex-end;
}
.message-container-wrapper {
width: calc(100% - 20px);
/* flex: 1; */
box-sizing: border-box;
/* padding: 5px 10px; */
border: 1px solid #ccc;
border-radius: 5px;
}
.message-container {
width: 100%;
max-width: 800px;
height: 100%;
max-height: 500px;
overflow-y: auto;
display: flex;
flex-direction: column;
padding: 10px 0 ;
}
.message-container-message {
width: 95%;
margin: 0 10px;
}
</style>
+43 -2
View File
@@ -1,11 +1,17 @@
<!-- 若route.params.id为new则为新建而非修改 -->
<template>
<div class="post-editor-wrapper">
<div class="post-editor-header">
<div class="post-editor-header-back" @click="handleBack">
<chevronDown></chevronDown>
返回
</div>
</div>
<editor v-model:title="title" v-model:summary="summary" v-model:tags="tags" v-model:content="content" />
<el-row>
<el-col :span="24">
<div class="flex-end">
<el-text v-if="message" :type="message.type">{{ message.message }}</el-text>
<el-text v-if="message" :type="message.type" style="user-select: none;">{{ message.message }}</el-text>
<el-button @click="handleSubmit" style="margin-left: 10px;">
upload
</el-button>
@@ -20,6 +26,7 @@ import editor from '~/components/user/postEditor.vue';
import useDefaultStore from '~/hooks/pinia/useDefaultStore';
import useFetch from '~/hooks/useMiaoFetch';
import type { postItem } from '~/types/post';
import chevronDown from "~/components/icons/chevronDown.vue";
type resType = {
data: string
@@ -29,6 +36,7 @@ type resType = {
const { post: { getPost, updatePost, uploadPost } } = useFetch()
const route = useRoute()
const router = useRouter()
const id = ref<number>(-1)
const title = ref<string>('')
@@ -48,6 +56,10 @@ const handleSubmit = () => {
const handleUpload = async () => {
try {
message.value = {
type: 'info',
message: '上传中'
}
const response = await uploadPost({
title: title.value,
summary: summary.value,
@@ -80,6 +92,10 @@ const handleUpload = async () => {
const handleUpdate = async () => {
try {
message.value = {
type: 'info',
message: '上传中'
}
const response = await updatePost({
id: id.value,
title: title.value,
@@ -108,6 +124,10 @@ const handleUpdate = async () => {
}
}
const handleBack = () => {
router.back()
}
onMounted(async () => {
console.log('route', route.params.id, route.params.id === 'new')
if (route.params.id !== 'new') {
@@ -123,7 +143,7 @@ onMounted(async () => {
})
</script>
<style scoped>
<style lang="scss" scoped>
.post-editor-wrapper {
/* box-sizing: border-box; */
display: flex;
@@ -132,6 +152,27 @@ onMounted(async () => {
padding: 20px;
}
.post-editor-header {
&-back {
width: fit-content;
display: flex;
justify-content: space-between;
align-items: center;
user-select: none;
svg {
width: 18px;
transform: rotate(90deg) translateX(1px);
margin-right: 3px;
}
&:hover {
cursor: pointer;
}
}
}
.flex-end {
display: flex;
justify-content: flex-end;
+29 -17
View File
@@ -2,23 +2,8 @@
<div>
its the page which is used to test the api
<div>
<el-button @click="uploadTest">
uploadPostTest
</el-button>
<el-button @click="editTest">
editPostTest
</el-button>
<el-button @click="testLogin">
testLogin
</el-button>
<el-button @click="testRegister">
testRegister
</el-button>
<el-button @click="testRefreshToken">
testRefreshToken
</el-button>
<el-button @click="testAuth">
testAuth
<el-button v-for="item in Object.entries(testFunctionList)" @click="item[1]">
{{ item[0] }}
</el-button>
</div>
</div>
@@ -90,6 +75,33 @@ const testRefreshToken = async () => {
userStore.setToken(token)
}
const testToolShiquLikeApi = async () => {
const id = 29729408
const likes = 3
const sse = new EventSource(`/api/tools/shiqu/like?id=${id}&likes=${likes}`);
sse.onopen = function (event) {
console.log('Connection opened');
};
sse.onmessage = function (event) {
console.log('Received message:', event.data);
};
sse.onerror = function (event) {
console.error('Error occurred:', event);
sse.close()
};
}
const testFunctionList = {
uploadTest,
editTest,
testLogin,
testRegister,
testRefreshToken,
testToolShiquLikeApi
}
onMounted(() => {
// testAuth()
})
+122
View File
@@ -0,0 +1,122 @@
import useMiaoSSE from "~/server/utils/hooks/useMiaoSSE"
async function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms))
}
export default defineEventHandler(async (event) => {
const sse = useMiaoSSE(event)
const query = getQuery(event)
const id = Number(query.id)
const likes = Number(query.likes)
const likeCountBefore = await getPraiseCount(id)
sse.write({
likeCountBefore,
})
for (let i = 0; i < likes; i++) {
await praise(id)
sse.write({
process: i + 1,
percent: Math.round((i + 1) / likes * 100)
})
await sleep(100)
}
const likeCountAfter = await getPraiseCount(id)
sse.write({
likeCountAfter,
})
return {
code: 200,
msg: 'success',
id, likes
}
})
/**
* Description 刷赞函数,运行一次刷1个赞
* @param {number} id 作品id
* @returns {number} 刷赞成功次数
*/
const praise = async (id: number): Promise<number> => {
const sid = generateRandomSid();
let success_count = 0;
try {
const response = await fetch(`https://shiqu.zhilehuo.com/api/soundRecord/praise?soundRecordId=${id}&paraSid=${sid}`, {
"headers": {
"accept": "application/json, text/plain, */*",
"accept-language": "zh-CN,zh;q=0.9",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"cookie": `sid=${sid}`,
"Referer": `https://shiqu.zhilehuo.com/page/record/soundRecordDetail.html?source=share&id=${id}&code=011CrS000ZlKxS1qQH300N6Ia90CrS0E&state=sq2019`,
"Referrer-Policy": "unsafe-url"
},
"body": null,
"method": "GET"
});
const result = await response.json();
if (result.code === 0) {
success_count += 1;
}
// if (success_count > 0) {
// console.log(`帮忙点赞${success_count}次`);
// }
return success_count;
} catch (e) {
console.error(e);
return -1;
}
};
/**
* Description 获取对应作品赞数
* @param {number} id 作品id
* @returns {number}
*/
const getPraiseCount = async (id: number): Promise<number> => {
try {
const response = await fetch(`https://shiqu.zhilehuo.com/api/soundRecord/getSoundRecordDetail?id=${id}&from=share&startDate=&endDate=&sg=`, {
"headers": {
"accept": "application/json, text/plain, */*",
"accept-language": "zh-CN,zh;q=0.9",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"cookie": `sid=${generateRandomSid()}`,
"Referer": `https://shiqu.zhilehuo.com/page/record/soundRecordDetail.html?source=share&id=${id}`,
"Referrer-Policy": "unsafe-url"
},
"body": null,
"method": "GET"
});
const result = await response.json();
return result.data.praiseCount;
} catch (e) {
console.error(e);
return -1;
}
};
/**
* Description 伪造sid
* @returns {string}
*/
const generateRandomSid = () => {
const characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
const separator = "-";
const length = 28;
let result = "";
for (let i = 0; i < length; i++) {
if (i === length / 2) {
result += separator;
} else {
const randomIndex = Math.floor(Math.random() * characters.length);
result += characters.charAt(randomIndex);
}
}
return result;
};
+25
View File
@@ -0,0 +1,25 @@
export default function useMiaoSSE(event: any) {
setResponseHeader(event, 'Content-Type', 'text/event-stream')
setResponseHeader(event, 'Cache-Control', 'no-cache')
setResponseHeader(event, 'Connection', 'keep-alive')
const messageData: {
id: number,
message: string
}[] = []
const _res = event.node.res
return {
write: (data: { [key: string]: any }) => {
const msg = `data: ${JSON.stringify(data)}\n\n`
const id = messageData.length
messageData.push({
id,
message: msg
})
_res.write(`id: ${id}\ndata: ${msg}\n\n`)
return id
}
}
}