@@ -8,7 +8,6 @@ import io.ktor.client.plugins.compression.*
88import io.ktor.client.request.*
99import io.ktor.client.statement.*
1010import io.ktor.http.*
11- import io.ktor.utils.io.charsets.*
1211import io.ktor.utils.io.core.*
1312import io.ktor.utils.io.errors.*
1413import kotlinx.coroutines.*
@@ -51,6 +50,12 @@ public object NetDisk : BaiduNetDiskClient(config = NetdiskOauthConfig), Listene
5150 }
5251 }
5352
53+ @JvmStatic
54+ public val SHORT_URL_REGEX : Regex = """ (?:surl=|s/1)([A-z0-9=_-]+)\W+([A-z0-9]{4})?""" .toRegex()
55+
56+ @JvmStatic
57+ public val STAND_CODE_REGEX : Regex = """ [A-z0-9]{32}#[A-z0-9]{32}#\d+#\S+""" .toRegex()
58+
5459 private val logger: MiraiLogger by lazy {
5560 try {
5661 NetDiskFileSyncPlugin .logger
@@ -71,9 +76,23 @@ public object NetDisk : BaiduNetDiskClient(config = NetdiskOauthConfig), Listene
7176 }
7277 }
7378
79+ private val downloader: HttpClient = HttpClient (OkHttp ) {
80+ ContentEncoding ()
81+ BrowserUserAgent ()
82+ // FIXME: MalformedInputException
83+ expectSuccess = NetdiskUploadConfig .https.not ()
84+ HttpResponseValidator {
85+ validateResponse { response ->
86+ if (response.headers[HttpHeaders .ContentType ] == " text/octet" ) {
87+ val bytes = response.readBytes()
88+ throw ClientRequestException (response, bytes.joinToString(" " ) { " \\ x%02x" .format(it) })
89+ }
90+ }
91+ }
92+ }
93+
7494 override val apiIgnore: suspend (Throwable ) -> Boolean = { throwable ->
7595 when (throwable) {
76- is MalformedInputException -> false
7796 is IOException -> {
7897 val count = ++ throwable::class .count
7998 if (count > 10 ) {
@@ -84,7 +103,6 @@ public object NetDisk : BaiduNetDiskClient(config = NetdiskOauthConfig), Listene
84103 true
85104 }
86105 }
87-
88106 else -> false
89107 }
90108 }
@@ -101,15 +119,15 @@ public object NetDisk : BaiduNetDiskClient(config = NetdiskOauthConfig), Listene
101119 }
102120
103121 @EventHandler
104- public fun MessageEvent.handle () {
122+ public fun MessageEvent.upload () {
105123 val contact = subject as ? Group ? : return
106124 val content = message.findIsInstance<FileMessage >() ? : return
107125 if (permission.testPermission(contact.permitteeId).not () &&
108126 permission.testPermission(sender.permitteeId).not ()
109127 ) return
110128
111129 launch {
112- logger.info { " 发现文件消息 ${ content} , 开始上传" }
130+ logger.info { " 发现文件消息 $content 开始上传" }
113131 val file = withTimeout(10_000 ) {
114132 content.toAbsoluteFile(contact) ? : throw NoSuchElementException (" ${content.name} in $contact " )
115133 }
@@ -134,15 +152,81 @@ public object NetDisk : BaiduNetDiskClient(config = NetdiskOauthConfig), Listene
134152 }
135153 }
136154
155+ @EventHandler
156+ public fun MessageEvent.save () {
157+ val plain = message.findIsInstance<PlainText >() ? : return
158+ if (permission.testPermission(sender.permitteeId).not ()) return
159+
160+ SHORT_URL_REGEX .findAll(plain.content).forEach { match ->
161+ launch {
162+ logger.info { " 发现分享链接 ${match.value} 开始转存" }
163+ val (surl, password) = match.destructured
164+ val key = if (password.isNotEmpty()) {
165+ val verify = rest.verify(surl = surl, password = password)
166+ require(verify.errorNo == 0 ) { verify.errorMessage.ifEmpty { surl } }
167+ verify.key
168+ } else {
169+ " "
170+ }
171+
172+ val root = rest.view(surl = surl, key = key)
173+
174+ val dir = rest.mkdir(path = " ${subject.id} /${root.uk} -${root.shareId} " , ondup = OnDupType .NEW_COPY )
175+
176+ val info = TransferFileInfo (
177+ shareId = root.shareId,
178+ from = root.uk,
179+ key = key,
180+ files = emptyList()
181+ )
182+
183+ val limit = user().vip.transferLimit
184+
185+ for (list in root.list.chunked(limit)) {
186+ val part = info.copy(files = list.map { it.id })
187+
188+ rest.transfer(info = part, path = dir.path, ondup = OnDupType .NEW_COPY )
189+
190+ delay(30_000 )
191+ }
192+
193+ launch {
194+ NetDiskFileSyncRecorder .record(source = source, surl = surl, password = password)
195+ }
196+ if (NetdiskUploadConfig .reply) {
197+ subject.sendMessage(message.quote() + " 分享 $surl 转存成功" )
198+ }
199+ }
200+ }
201+ STAND_CODE_REGEX .findAll(plain.content).forEach { match ->
202+ launch {
203+ logger.info { " 发现秒传码 ${match.value} 开始保存" }
204+ mkdir(path = " ${subject.id} " )
205+ val upload = RapidUploadInfo .parse(code = match.value)
206+ val path = " ${subject.id} /${upload.path.substringAfterLast(' /' )} "
207+
208+ rapid(upload = upload.copy(path = path), ondup = OnDupType .NEW_COPY )
209+
210+ launch {
211+ NetDiskFileSyncRecorder .record(source = source, rapid = upload)
212+ }
213+ if (NetdiskUploadConfig .reply) {
214+ subject.sendMessage(message.quote() + " 保存成功" )
215+ }
216+ }
217+ }
218+ }
219+
137220 private suspend fun uploadAbsoluteFile (file : AbsoluteFile ): RapidUploadInfo {
138221 val url = requireNotNull(file.getUrl()) { " 远程文件 URL 获取失败" }
222+ @Suppress(" INVISIBLE_MEMBER" )
139223 val rapid = with (file) {
140- val content = file.md5.toUHexString( " " ).lowercase ()
224+ val content = file.md5.toHexString ()
141225 val slice = if (size <= SLICE_SIZE ) {
142226 content
143227 } else {
144228 val slice = download(urlString = url, range = 0L until SLICE_SIZE .toLong()).body<ByteArray >()
145- slice.md5().toUHexString( " " ).lowercase ()
229+ slice.md5().toHexString ()
146230 }
147231 RapidUploadInfo (
148232 content = content,
@@ -166,9 +250,9 @@ public object NetDisk : BaiduNetDiskClient(config = NetdiskOauthConfig), Listene
166250 logger.warning({ " 文件 ${file.name} 秒传失败, 进入文件上传" }, exception)
167251 }
168252
169- val user = rest.user()
170- check(file.size <= user.vip.updateLimit) { " ${file.contact} -${file.name} 超过了文件上传极限" }
253+ val user = user()
171254 val limit = user.vip.superLimit.toLong()
255+ check(file.size <= limit) { " ${file.contact} -${file.name} 超过了文件上传极限" }
172256
173257 if (file.size < limit) {
174258 try {
@@ -180,15 +264,13 @@ public object NetDisk : BaiduNetDiskClient(config = NetdiskOauthConfig), Listene
180264 } catch (throwable: ClientRequestException ) {
181265 logger.info { " 文件 ${file.name} 单文件上传失败, 进入文件上传, ${throwable.message} " }
182266 } catch (exception: Throwable ) {
183- logger.info({ " 文件 ${file.name} 快速存入失败 , 进入文件上传" }, exception)
267+ logger.info({ " 文件 ${file.name} 单文件上传失败 , 进入文件上传" }, exception)
184268 }
185269 }
186270
187271
188272 val prepare = rest.prepare(upload = rapid, blocks = LAZY_BLOCKS , ondup = OnDupType .NEW_COPY )
189- if (prepare.type == PrepareReturnType .EXIST ) {
190- return rapid
191- }
273+ if (prepare.type == PrepareReturnType .EXIST ) return rapid
192274 val uploadId = requireNotNull(prepare.uploadId) { prepare }
193275
194276 val blocks = download(urlString = url).execute { response ->
@@ -211,7 +293,7 @@ public object NetDisk : BaiduNetDiskClient(config = NetdiskOauthConfig), Listene
211293 }.awaitAll()
212294
213295 val merge = MergeFileInfo (
214- blocks = blocks.toMutableList() ,
296+ blocks = blocks,
215297 uploadId = uploadId,
216298 size = file.size,
217299 path = rapid.path
@@ -222,21 +304,6 @@ public object NetDisk : BaiduNetDiskClient(config = NetdiskOauthConfig), Listene
222304 return rapid
223305 }
224306
225- private val downloader: HttpClient = HttpClient (OkHttp ) {
226- ContentEncoding ()
227- BrowserUserAgent ()
228- // FIXME: MalformedInputException
229- expectSuccess = NetdiskUploadConfig .https.not ()
230- HttpResponseValidator {
231- validateResponse { response ->
232- if (response.headers[HttpHeaders .ContentType ] == " text/octet" ) {
233- val bytes = response.readBytes()
234- throw ClientRequestException (response, bytes.joinToString(" " ) { " \\ x%02x" .format(it) })
235- }
236- }
237- }
238- }
239-
240307 private suspend fun download (urlString : String , range : LongRange ? = null): HttpStatement {
241308 val fragment = range?.run { " bytes=${start} -${endInclusive} " }
242309 logger.verbose { " download $urlString #$fragment " }
0 commit comments