弹幕:广播带半显用户名(前两字+***),前端展示前缀
Made-with: Cursor
This commit is contained in:
@@ -61,6 +61,19 @@ func SiteDanmakuTokenValid(tokenStr string) bool {
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MaskSiteUsernameForDanmaku 弹幕展示半匿名:1 字为「a***」,2 字及以上为前两字 + ***(如 aa***、ab***)
|
||||||
|
func MaskSiteUsernameForDanmaku(username string) string {
|
||||||
|
username = strings.TrimSpace(username)
|
||||||
|
if username == "" {
|
||||||
|
return "***"
|
||||||
|
}
|
||||||
|
runes := []rune(username)
|
||||||
|
if len(runes) == 1 {
|
||||||
|
return string(runes[0]) + "***"
|
||||||
|
}
|
||||||
|
return string(runes[:2]) + "***"
|
||||||
|
}
|
||||||
|
|
||||||
type siteRegisterInput struct {
|
type siteRegisterInput struct {
|
||||||
Username string `json:"username" binding:"required"`
|
Username string `json:"username" binding:"required"`
|
||||||
Password string `json:"password" binding:"required"`
|
Password string `json:"password" binding:"required"`
|
||||||
|
|||||||
@@ -28,9 +28,14 @@ func writeDanmakuJSON(ws *websocket.Conn, v any) error {
|
|||||||
return ws.WriteMessage(websocket.TextMessage, b)
|
return ws.WriteMessage(websocket.TextMessage, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleDanmakuWS 弹幕:收 JSON {"text":"..."};未带有效 token 仅可收广播不可发。广播 {"type":"dm","text","ts"},不落库
|
// handleDanmakuWS 弹幕:收 JSON {"text":"..."};未带有效 token 仅可收广播不可发。广播 {"type":"dm","text","from","ts"},不落库
|
||||||
func handleDanmakuWS(c *gin.Context) {
|
func handleDanmakuWS(c *gin.Context) {
|
||||||
canSend := handlers.SiteDanmakuTokenValid(c.Query("token"))
|
claims, tokenOK := handlers.ParseSiteClaims(c.Query("token"))
|
||||||
|
canSend := tokenOK
|
||||||
|
fromDisplay := "***"
|
||||||
|
if tokenOK && claims != nil {
|
||||||
|
fromDisplay = handlers.MaskSiteUsernameForDanmaku(claims.Username)
|
||||||
|
}
|
||||||
|
|
||||||
ws, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
ws, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -72,6 +77,7 @@ func handleDanmakuWS(c *gin.Context) {
|
|||||||
out, err := json.Marshal(map[string]interface{}{
|
out, err := json.Marshal(map[string]interface{}{
|
||||||
"type": "dm",
|
"type": "dm",
|
||||||
"text": text,
|
"text": text,
|
||||||
|
"from": fromDisplay,
|
||||||
"ts": time.Now().UnixMilli(),
|
"ts": time.Now().UnixMilli(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
class="live-dm-line"
|
class="live-dm-line"
|
||||||
:style="{ top: d.top + '%' }"
|
:style="{ top: d.top + '%' }"
|
||||||
>
|
>
|
||||||
{{ d.text }}
|
<span class="live-dm-from">{{ d.from }}:</span>{{ d.text }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="live-video-toolbar" role="toolbar" aria-label="播放控制">
|
<div class="live-video-toolbar" role="toolbar" aria-label="播放控制">
|
||||||
@@ -193,10 +193,12 @@ function toggleVideoFullscreen() {
|
|||||||
el.requestFullscreen?.() || el.webkitRequestFullscreen?.()
|
el.requestFullscreen?.() || el.webkitRequestFullscreen?.()
|
||||||
}
|
}
|
||||||
|
|
||||||
function pushDmLine(text) {
|
function pushDmLine(text, fromRaw) {
|
||||||
|
const from =
|
||||||
|
typeof fromRaw === 'string' && fromRaw.trim() !== '' ? fromRaw.trim() : '***'
|
||||||
const id = ++dmIdSeq
|
const id = ++dmIdSeq
|
||||||
const top = 8 + Math.floor(Math.random() * 55)
|
const top = 8 + Math.floor(Math.random() * 55)
|
||||||
dmItems.value = [...dmItems.value, { id, text, top }]
|
dmItems.value = [...dmItems.value, { id, text, top, from }]
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
dmItems.value = dmItems.value.filter((x) => x.id !== id)
|
dmItems.value = dmItems.value.filter((x) => x.id !== id)
|
||||||
}, 12000)
|
}, 12000)
|
||||||
@@ -272,7 +274,7 @@ function connectDanmaku() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (j.type === 'dm' && typeof j.text === 'string' && j.text) {
|
if (j.type === 'dm' && typeof j.text === 'string' && j.text) {
|
||||||
pushDmLine(j.text)
|
pushDmLine(j.text, j.from)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (j.type === 'error' && j.code === 'login_required') {
|
if (j.type === 'error' && j.code === 'login_required') {
|
||||||
@@ -459,6 +461,11 @@ onUnmounted(() => {
|
|||||||
text-shadow: 0 0 6px #000, 0 0 2px #000;
|
text-shadow: 0 0 6px #000, 0 0 2px #000;
|
||||||
animation: live-dm-marquee 12s linear forwards;
|
animation: live-dm-marquee 12s linear forwards;
|
||||||
}
|
}
|
||||||
|
.live-dm-from {
|
||||||
|
color: #7fdbff;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
@keyframes live-dm-marquee {
|
@keyframes live-dm-marquee {
|
||||||
from {
|
from {
|
||||||
transform: translateX(105%);
|
transform: translateX(105%);
|
||||||
|
|||||||
Reference in New Issue
Block a user