Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Y
ygopro-tips-biu-generator
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nanahira
ygopro-tips-biu-generator
Commits
1006a158
Commit
1006a158
authored
Sep 21, 2021
by
nanahira
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
rework
parent
1e98a3df
Pipeline
#5728
passed with stages
in 33 minutes and 18 seconds
Changes
7
Pipelines
1
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
11142 additions
and
156 deletions
+11142
-156
Dockerfile
Dockerfile
+1
-1
package-lock.json
package-lock.json
+11074
-84
package.json
package.json
+3
-4
src/app.controller.ts
src/app.controller.ts
+2
-1
src/app.logger.ts
src/app.logger.ts
+0
-4
src/app.module.ts
src/app.module.ts
+4
-3
src/app.service.ts
src/app.service.ts
+58
-59
No files found.
Dockerfile
View file @
1006a158
FROM
node:bu
ster
-slim
FROM
node:bu
llseye
-slim
RUN
apt update
&&
apt
-y
install
python3 build-essential
&&
rm
-rf
/var/lib/apt/lists/
*
/tmp/
*
/var/tmp/
*
RUN
apt update
&&
apt
-y
install
python3 build-essential
&&
rm
-rf
/var/lib/apt/lists/
*
/tmp/
*
/var/tmp/
*
WORKDIR
/usr/src/app
WORKDIR
/usr/src/app
COPY
./package*.json ./
COPY
./package*.json ./
...
...
package-lock.json
View file @
1006a158
This diff is collapsed.
Click to expand it.
package.json
View file @
1006a158
...
@@ -21,25 +21,24 @@
...
@@ -21,25 +21,24 @@
"test:e2e"
:
"jest --config ./test/jest-e2e.json"
"test:e2e"
:
"jest --config ./test/jest-e2e.json"
},
},
"dependencies"
:
{
"dependencies"
:
{
"
@nestjs/axios
"
:
"
^0.0.1
"
,
"
@nestjs/common
"
:
"
^7.5.1
"
,
"
@nestjs/common
"
:
"
^7.5.1
"
,
"
@nestjs/config
"
:
"
^1.0.1
"
,
"
@nestjs/core
"
:
"
^7.5.1
"
,
"
@nestjs/core
"
:
"
^7.5.1
"
,
"
@nestjs/platform-express
"
:
"
^7.5.1
"
,
"
@nestjs/platform-express
"
:
"
^7.5.1
"
,
"
@nestjs/swagger
"
:
"
^4.8.0
"
,
"
@nestjs/swagger
"
:
"
^4.8.0
"
,
"
@nestjs/typeorm
"
:
"
^7.1.5
"
,
"
@nestjs/typeorm
"
:
"
^7.1.5
"
,
"
@types/cookie
"
:
"
^0.4.0
"
,
"
@types/cookie
"
:
"
^0.4.0
"
,
"
axios
"
:
"
^0.21.1
"
,
"
class-transformer
"
:
"
^0.4.0
"
,
"
class-transformer
"
:
"
^0.4.0
"
,
"
class-validator
"
:
"
^0.13.1
"
,
"
class-validator
"
:
"
^0.13.1
"
,
"
cookie
"
:
"
^0.4.1
"
,
"
cookie
"
:
"
^0.4.1
"
,
"
lodash
"
:
"
^4.17.21
"
,
"
lodash
"
:
"
^4.17.21
"
,
"
moment
"
:
"
^2.29.1
"
,
"
moment
"
:
"
^2.29.1
"
,
"
mysql
"
:
"
^2.18.1
"
,
"
p-queue
"
:
"
^6.6.2
"
,
"
p-queue
"
:
"
^6.6.2
"
,
"
reflect-metadata
"
:
"
^0.1.13
"
,
"
reflect-metadata
"
:
"
^0.1.13
"
,
"
rimraf
"
:
"
^3.0.2
"
,
"
rimraf
"
:
"
^3.0.2
"
,
"
rxjs
"
:
"
^6.6.3
"
,
"
rxjs
"
:
"
^6.6.3
"
,
"
swagger-ui-express
"
:
"
^4.1.6
"
,
"
swagger-ui-express
"
:
"
^4.1.6
"
"
typeorm
"
:
"
^0.2.32
"
},
},
"devDependencies"
:
{
"devDependencies"
:
{
"
@nestjs/cli
"
:
"
^7.5.1
"
,
"
@nestjs/cli
"
:
"
^7.5.1
"
,
...
...
src/app.controller.ts
View file @
1006a158
import
{
Controller
,
Get
}
from
'
@nestjs/common
'
;
import
{
Controller
,
Get
}
from
'
@nestjs/common
'
;
import
{
AppService
}
from
'
./app.service
'
;
import
{
AppService
}
from
'
./app.service
'
;
import
{
ApiOperation
,
ApiTags
}
from
'
@nestjs/swagger
'
;
import
{
ApiO
kResponse
,
ApiO
peration
,
ApiTags
}
from
'
@nestjs/swagger
'
;
@
Controller
()
@
Controller
()
@
ApiTags
(
'
tips
'
)
@
ApiTags
(
'
tips
'
)
...
@@ -12,6 +12,7 @@ export class AppController {
...
@@ -12,6 +12,7 @@ export class AppController {
summary
:
'
获取提示
'
,
summary
:
'
获取提示
'
,
description
:
'
获取 SRVPro 需要的 tips.json 文件。
'
,
description
:
'
获取 SRVPro 需要的 tips.json 文件。
'
,
})
})
@
ApiOkResponse
({
type
:
[
String
]
})
async
getTips
():
Promise
<
string
[]
>
{
async
getTips
():
Promise
<
string
[]
>
{
return
await
this
.
appService
.
getTips
();
return
await
this
.
appService
.
getTips
();
}
}
...
...
src/app.logger.ts
deleted
100644 → 0
View file @
1e98a3df
import
{
Injectable
,
Scope
,
Logger
}
from
'
@nestjs/common
'
;
@
Injectable
()
export
class
AppLogger
extends
Logger
{}
src/app.module.ts
View file @
1006a158
import
{
Module
}
from
'
@nestjs/common
'
;
import
{
Module
}
from
'
@nestjs/common
'
;
import
{
AppController
}
from
'
./app.controller
'
;
import
{
AppController
}
from
'
./app.controller
'
;
import
{
AppService
}
from
'
./app.service
'
;
import
{
AppService
}
from
'
./app.service
'
;
import
{
AppLogger
}
from
'
./app.logger
'
;
import
{
HttpModule
}
from
'
@nestjs/axios
'
;
import
{
ConfigModule
}
from
'
@nestjs/config
'
;
@
Module
({
@
Module
({
imports
:
[],
imports
:
[
HttpModule
,
ConfigModule
.
forRoot
()
],
controllers
:
[
AppController
],
controllers
:
[
AppController
],
providers
:
[
AppService
,
AppLogger
],
providers
:
[
AppService
],
})
})
export
class
AppModule
{}
export
class
AppModule
{}
src/app.service.ts
View file @
1006a158
import
{
Injectable
}
from
'
@nestjs/common
'
;
import
{
Injectable
,
Logger
}
from
'
@nestjs/common
'
;
import
{
AppLogger
}
from
'
./app.logger
'
;
import
moment
from
'
moment
'
;
import
moment
from
'
moment
'
;
import
PQueue
from
'
p-queue
'
;
import
PQueue
from
'
p-queue
'
;
import
axios
from
'
axios
'
;
import
_
from
'
lodash
'
;
import
_
from
'
lodash
'
;
import
qs
from
'
qs
'
;
import
qs
from
'
qs
'
;
import
cookie
from
'
cookie
'
;
import
cookie
from
'
cookie
'
;
import
{
ConfigService
}
from
'
@nestjs/config
'
;
import
{
HttpService
}
from
'
@nestjs/axios
'
;
const
blacklistedWords
=
[
'
曲
'
,
'
词
'
,
'
曲
'
,
'
詞
'
,
'
[Vv]ocal
'
,
'
:
'
,
'
:
'
];
const
blacklistedWords
=
[
'
曲
'
,
'
词
'
,
'
曲
'
,
'
詞
'
,
'
[Vv]ocal
'
,
'
:
'
,
'
:
'
];
const
blacklistedRegex
=
blacklistedWords
.
map
((
word
)
=>
new
RegExp
(
word
));
const
blacklistedRegex
=
blacklistedWords
.
map
((
word
)
=>
new
RegExp
(
word
));
...
@@ -16,55 +16,43 @@ interface LoginInfo {
...
@@ -16,55 +16,43 @@ interface LoginInfo {
}
}
@
Injectable
()
@
Injectable
()
export
class
AppService
{
export
class
AppService
extends
Logger
{
cache
:
string
[];
cache
:
string
[];
lastFetchTime
:
moment
.
Moment
;
lastFetchTime
:
moment
.
Moment
;
queue
:
PQueue
;
queue
=
new
PQueue
({
concurrency
:
1
})
;
loginInfo
:
LoginInfo
;
loginInfo
:
LoginInfo
;
constructor
(
private
log
:
AppLogger
)
{
constructor
(
config
:
ConfigService
,
private
http
:
HttpService
)
{
this
.
log
.
setContext
(
'
ygopro-tips-biu-generator
'
);
super
(
'
ygopro-tips-biu-generator
'
);
this
.
queue
=
new
PQueue
({
concurrency
:
1
});
if
(
config
.
get
<
string
>
(
'
BIU_EMAIL
'
)
&&
config
.
get
<
string
>
(
'
BIU_PASSWORD
'
))
{
if
(
process
.
env
.
BIU_EMAIL
&&
process
.
env
.
BIU_PASSWORD
)
{
this
.
loginInfo
=
{
this
.
loginInfo
=
{
email
:
process
.
env
.
BIU_EMAIL
,
email
:
config
.
get
<
string
>
(
'
BIU_EMAIL
'
)
,
password
:
process
.
env
.
BIU_PASSWORD
,
password
:
config
.
get
<
string
>
(
'
BIU_PASSWORD
'
)
,
};
};
}
}
}
}
async
getTips
():
Promise
<
string
[]
>
{
async
getTips
():
Promise
<
string
[]
>
{
if
(
return
this
.
fetchTips
();
this
.
cache
&&
this
.
lastFetchTime
&&
moment
().
diff
(
this
.
lastFetchTime
)
<=
3600000
)
{
return
this
.
cache
;
}
this
.
log
.
log
(
`Queue size:
${
this
.
queue
.
size
}
`
);
if
(
this
.
queue
.
size
>
0
)
{
await
this
.
queue
.
onEmpty
();
return
this
.
cache
;
}
else
{
return
await
this
.
fetchTips
();
}
}
}
async
fetchTips
():
Promise
<
string
[]
>
{
async
fetchTips
():
Promise
<
string
[]
>
{
return
await
this
.
queue
.
add
(
async
()
=>
await
this
.
fetchTipsTask
());
return
await
this
.
queue
.
add
(
async
()
=>
this
.
fetchTipsTask
());
}
}
async
getLyricsOfSong
(
id
:
number
):
Promise
<
string
[]
>
{
async
getLyricsOfSong
(
id
:
number
):
Promise
<
string
[]
>
{
try
{
try
{
this
.
log
.
log
(
`Fetching lyrics of song
${
id
}
.`
);
this
.
log
(
`Fetching lyrics of song
${
id
}
.`
);
const
{
data
}
=
await
axios
.
get
(
`https://web.biu.moe/s
${
id
}
`
,
{
const
{
data
}
=
await
this
.
http
responseType
:
'
document
'
,
.
get
(
`https://web.biu.moe/s
${
id
}
`
,
{
timeout
:
30000
,
responseType
:
'
document
'
,
});
timeout
:
30000
,
})
.
toPromise
();
const
lyricMatches
=
(
data
as
string
).
match
(
const
lyricMatches
=
(
data
as
string
).
match
(
/
\[\d
+:
\d
+
[
:
\.]\d
+
\]([^
<
]
+
)
/g
,
/
\[\d
+:
\d
+
[
:
\.]\d
+
\]([^
<
]
+
)
/g
,
);
);
if
(
!
lyricMatches
)
{
if
(
!
lyricMatches
)
{
this
.
log
.
log
(
`Song
${
id
}
has no lyrics.`
);
this
.
log
(
`Song
${
id
}
has no lyrics.`
);
return
[];
return
[];
}
}
const
lyrics
=
lyricMatches
const
lyrics
=
lyricMatches
...
@@ -74,10 +62,10 @@ export class AppService {
...
@@ -74,10 +62,10 @@ export class AppService {
.
filter
((
lyric
)
=>
.
filter
((
lyric
)
=>
blacklistedRegex
.
every
((
regex
)
=>
!
lyric
.
match
(
regex
)),
blacklistedRegex
.
every
((
regex
)
=>
!
lyric
.
match
(
regex
)),
);
);
this
.
log
.
log
(
`
${
lyrics
.
length
}
lines of lyrics found in song
${
id
}
.`
);
this
.
log
(
`
${
lyrics
.
length
}
lines of lyrics found in song
${
id
}
.`
);
return
lyrics
;
return
lyrics
;
}
catch
(
e
)
{
}
catch
(
e
)
{
this
.
log
.
error
(
`Failed fetching lyrics of
${
id
}
:
${
e
.
toString
()}
`
);
this
.
error
(
`Failed fetching lyrics of
${
id
}
:
${
e
.
toString
()}
`
);
return
[];
return
[];
}
}
}
}
...
@@ -87,17 +75,19 @@ export class AppService {
...
@@ -87,17 +75,19 @@ export class AppService {
return
undefined
;
return
undefined
;
}
}
try
{
try
{
this
.
log
.
log
(
`Logging in.`
);
this
.
log
(
`Logging in.`
);
const
ret
=
await
axios
.
post
(
const
ret
=
await
this
.
http
'
https://web.biu.moe/User/doLogin
'
,
.
post
(
qs
.
stringify
(
this
.
loginInfo
),
'
https://web.biu.moe/User/doLogin
'
,
{
qs
.
stringify
(
this
.
loginInfo
),
responseType
:
'
json
'
,
{
timeout
:
30000
,
responseType
:
'
json
'
,
},
timeout
:
30000
,
);
},
)
.
toPromise
();
if
(
!
ret
.
data
.
status
)
{
if
(
!
ret
.
data
.
status
)
{
this
.
log
.
error
(
`Login failed:
${
ret
.
data
.
message
}
`
);
this
.
error
(
`Login failed:
${
ret
.
data
.
message
}
`
);
return
undefined
;
return
undefined
;
}
}
const
setCookieContent
=
ret
.
headers
[
'
set-cookie
'
]
as
string
[];
const
setCookieContent
=
ret
.
headers
[
'
set-cookie
'
]
as
string
[];
...
@@ -109,45 +99,54 @@ export class AppService {
...
@@ -109,45 +99,54 @@ export class AppService {
return
undefined
;
return
undefined
;
}
}
const
token
=
cookie
.
parse
(
contentHits
).
biuInfo
as
string
;
const
token
=
cookie
.
parse
(
contentHits
).
biuInfo
as
string
;
this
.
log
.
log
(
`Login success:
${
token
}
`
);
this
.
log
(
`Login success:
${
token
}
`
);
return
cookie
.
serialize
(
'
biuInfo
'
,
token
);
return
cookie
.
serialize
(
'
biuInfo
'
,
token
);
}
catch
(
e
)
{
}
catch
(
e
)
{
this
.
log
.
error
(
`Login error:
${
e
.
toString
()}
`
);
this
.
error
(
`Login error:
${
e
.
toString
()}
`
);
return
undefined
;
return
undefined
;
}
}
}
}
async
fetchTipsTask
():
Promise
<
string
[]
>
{
async
fetchTipsTask
():
Promise
<
string
[]
>
{
if
(
this
.
cache
&&
this
.
lastFetchTime
&&
moment
().
diff
(
this
.
lastFetchTime
)
<=
3600000
)
{
return
this
.
cache
;
}
try
{
try
{
this
.
log
.
log
(
`Fetching song list.`
);
this
.
log
(
`Fetching song list.`
);
const
{
data
}
=
await
axios
.
get
(
'
https://web.biu.moe/Index/home
'
,
{
const
{
data
}
=
await
this
.
http
responseType
:
'
document
'
,
.
get
(
'
https://web.biu.moe/Index/home
'
,
{
headers
:
{
responseType
:
'
document
'
,
Cookie
:
(
await
this
.
getLoginCookie
())
||
''
,
headers
:
{
},
Cookie
:
(
await
this
.
getLoginCookie
())
||
''
,
timeout
:
30000
,
},
});
timeout
:
30000
,
})
.
toPromise
();
const
urls
=
(
data
as
string
).
match
(
/href="
\/
s
(\d
+
)
"/g
);
const
urls
=
(
data
as
string
).
match
(
/href="
\/
s
(\d
+
)
"/g
);
if
(
!
urls
)
{
if
(
!
urls
)
{
this
.
log
.
error
(
`No songs found, retrying.`
);
this
.
error
(
`No songs found, retrying.`
);
return
await
this
.
fetchTipsTask
();
return
await
this
.
fetchTipsTask
();
}
}
this
.
log
.
log
(
`
${
urls
.
length
}
songs found.`
);
this
.
log
(
`
${
urls
.
length
}
songs found.`
);
const
songIds
=
urls
.
map
((
m
)
=>
parseInt
(
m
.
match
(
/href="
\/
s
(\d
+
)
"/
)[
1
]));
const
songIds
=
urls
.
map
((
m
)
=>
parseInt
(
m
.
match
(
/href="
\/
s
(\d
+
)
"/
)[
1
]));
const
lyrics
=
_
.
flatten
(
const
lyrics
=
_
.
flatten
(
await
Promise
.
all
(
songIds
.
map
((
id
)
=>
this
.
getLyricsOfSong
(
id
))),
await
Promise
.
all
(
songIds
.
map
((
id
)
=>
this
.
getLyricsOfSong
(
id
))),
);
);
this
.
log
.
log
(
`
${
lyrics
.
length
}
lines of lyrics got.`
);
this
.
log
(
`
${
lyrics
.
length
}
lines of lyrics got.`
);
if
(
lyrics
.
length
)
{
if
(
lyrics
.
length
)
{
this
.
cache
=
lyrics
;
this
.
cache
=
lyrics
;
this
.
lastFetchTime
=
moment
();
this
.
lastFetchTime
=
moment
();
return
lyrics
;
return
lyrics
;
}
else
{
}
else
{
this
.
log
.
log
(
`No lyrics found, retrying.`
);
this
.
log
(
`No lyrics found, retrying.`
);
return
await
this
.
fetchTipsTask
();
return
await
this
.
fetchTipsTask
();
}
}
}
catch
(
e
)
{
}
catch
(
e
)
{
this
.
log
.
error
(
`Errored fetching song list, retrying:
${
e
.
toString
()}
`
);
this
.
error
(
`Errored fetching song list, retrying:
${
e
.
toString
()}
`
);
return
await
this
.
fetchTipsTask
();
return
await
this
.
fetchTipsTask
();
}
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment