♻️ 重构结构化帖子类型 #51

This commit is contained in:
BTMuli
2023-10-28 14:21:40 +08:00
parent b65afba30b
commit ecb0f1a793
3 changed files with 418 additions and 407 deletions

View File

@@ -1,12 +1,12 @@
/** /**
* @file plugins/Mys/types/post.d.ts * @file plugins/Mys/types/post.d.ts
* @description Mys 插件帖子类型定义文件 * @description Mys 插件帖子类型定义文件
* @since Beta v0.3.3 * @since Beta v0.3.4
*/ */
/** /**
* @description Mys 插件帖子类型 * @description Mys 插件帖子类型
* @since Alpha v0.2.1 * @since Beta v0.3.4
* @namespace TGApp.Plugins.Mys.Post * @namespace TGApp.Plugins.Mys.Post
* @memberof TGApp.Plugins.Mys * @memberof TGApp.Plugins.Mys
*/ */
@@ -103,7 +103,7 @@ declare namespace TGApp.Plugins.Mys.Post {
* @property {string} reply_time 最后回复时间 // "2023-03-05 20:26:54" * @property {string} reply_time 最后回复时间 // "2023-03-05 20:26:54"
* @property {number} is_deleted 是否删除 * @property {number} is_deleted 是否删除
* @property {boolean} is_interactive 是否互动 * @property {boolean} is_interactive 是否互动
* @property {string} structured_content 结构化内容 // 反序列化后为 PostStructuredContent * @property {string} structured_content 结构化内容 // 反序列化后为 TGApp.Plugins.Mys.SctPost.Common[]
* @property {string[]} structured_content_rows 结构化内容原始数据 * @property {string[]} structured_content_rows 结构化内容原始数据
* @property {number} review_id 审核ID * @property {number} review_id 审核ID
* @property {boolean} is_profit 是否盈利 * @property {boolean} is_profit 是否盈利
@@ -298,172 +298,4 @@ declare namespace TGApp.Plugins.Mys.Post {
describe: string; describe: string;
images?: string[]; images?: string[];
} }
/**
* @description 帖子结构化内容
* @since Beta v0.3.3
* @todo 重构
* @interface StructuredContent
* @property {string|object} insert 插入内容
* @property {string} insert.image 图片 URL
* @property {StructuredVod} insert.vod 视频信息
* @property {string} insert.video 外部视频 URL
* @property {string} insert.backup_text 折叠文本
* @property {object} insert.lottery 抽奖,当 backup_text 为 [抽奖]
* @property {string} insert.lottery.id 抽奖 ID
* @property {string} insert.lottery.toast 抽奖提示
* @property {object} insert.fold 折叠内容
* @property {string} insert.fold.title 折叠标题,反序列化后为 PostStructuredContent[]
* @property {string} insert.fold.content 折叠文本,反序列化后为 PostStructuredContent[]
* @property {StructuredLinkCard} insert.link_card 链接卡片
* @property {string} insert.divider 分割线
* @property {object} insert.mention 提及
* @property {string} insert.mention.uid 用户 ID
* @property {string} insert.mention.nickname 用户昵称
* @property {StructuredVillaCard} insert.villa_card 大别野卡片
* @property {object} attributes 属性
* @property {number} attributes.height 高度
* @property {number} attributes.width 宽度
* @property {number} attributes.size 大小
* @property {string} attributes.ext 扩展名
* @property {boolean} attributes.bold 是否加粗
* @property {string} attributes.color 颜色
* @property {string} attributes.link 链接
* @return StructuredContent
*/
interface StructuredContent {
insert:
| {
image?: string;
video?: string;
vod?: StructuredVod;
backup_text?: string;
lottery?: {
id: string;
toast: string;
};
fold?: {
title: string;
content: string;
};
link_card?: StructuredLinkCard;
divider?: string;
mention?: {
uid: string;
nickname: string;
};
villa_card?: StructuredVillaCard;
}
| string;
attributes?: {
height?: number;
width?: number;
size?: number;
ext?: string;
bold?: boolean;
color?: string;
link?: string;
};
}
/**
* @description 帖子结构化内容-视频
* @since Alpha v0.2.2
* @interface StructuredVod
* @property {number} id 视频 ID
* @property {number} duration 时长
* @property {string} cover 封面图 URL
* @property {object[]} resolutions 分辨率
* @property {string} resolutions.url URL
* @property {string} resolutions.definition 清晰度
* @property {number} resolutions.height 高度
* @property {number} resolutions.width 宽度
* @property {number} resolutions.bitrate 比特率
* @property {number} resolutions.size 大小
* @property {string} resolutions.format 格式
* @property {string} resolutions.label 标签
* @property {number} view_num 浏览数
* @property {number} transcode_status 转码状态
* @property {number} review_status 审核状态
* @return StructuredVod
*/
interface StructuredVod {
id: number;
duration: number;
cover: string;
resolutions: Array<{
url: string;
definition: string;
height: number;
width: number;
bitrate: number;
size: number;
format: string;
label: string;
}>;
view_num: number;
transcode_status: number;
review_status: number;
}
/**
* @description 帖子结构化内容-链接卡片
* @since Alpha v0.2.1
* @interface StructuredLinkCard
* @property {number} link_type 链接类型 // 1: 帖子2商品
* @property {string} origin_url 原始链接
* @property {string} landing_url 落地页链接
* @property {string} cover 封面图 URL
* @property {string} title 标题
* @property {string} card_id 卡片 ID
* @property {number} card_status 卡片状态
* @property {string} market_price 市场价
* @property {string} price 价格
* @property {string} button_text 按钮文本
* @property {number} landing_url_type 落地链接类型
* @return StructuredLinkCard
*/
interface StructuredLinkCard {
link_type: number;
origin_url: string;
landing_url: string;
cover: string;
title: string;
card_id: string;
card_status: number;
market_price: string;
price: string;
button_text: string;
landing_url_type: number;
}
/**
* @description 帖子结构化内容-大别野卡片
* @since Beta v0.3.3
* @interface StructuredVillaCard
* @property {string} villa_id 大别野房间 ID
* @property {string} villa_name 大别野房间 名称
* @property {string} villa_avatar_url 大别野房间 头像图 URL
* @property {string} villa_cover 大别野房间 封面图 URL
* @property {string} owner_uid 大别野房间 房主 UID
* @property {string} owner_nickname 大别野房间 房主昵称
* @property {string} owner_avatar_url 大别野房间 房主头像图 URL
* @property {string} villa_introduce 大别野房间 介绍
* @property {string[]} tag_list 大别野房间 标签列表
* @property {string} villa_member_num 大别野房间 成员数
* @property {boolean} is_available 大别野房间 是否可用
* @return StructuredVillaCard
*/
interface StructuredVillaCard {
villa_id: string;
villa_name: string;
villa_avatar_url: string;
villa_cover: string;
owner_uid: string;
owner_nickname: string;
owner_avatar_url: string;
villa_introduce: string;
tag_list: string[];
villa_member_num: string;
}
} }

319
src/plugins/Mys/types/SctPost.d.ts vendored Normal file
View File

@@ -0,0 +1,319 @@
/**
* @file plugins/Mys/types/SctPost.d.ts
* @description Mys 插件 结构化帖子类型声明文件
* @todo 完善类型
* @since Beta v0.3.4
*/
/**
* @description 结构化帖子类型命名空间
* @since Beta v0.3.4
* @namespace TGApp.Plugins.Mys.SctPost
* @memberof TGApp.Plugins.Mys
*/
declare namespace TGApp.Plugins.Mys.SctPost {
/**
* @description 帖子结构化数据-基础类型
* @since Beta v0.3.4
* @interface Base
* @property {unknown} insert - 帖子内容
* @property {unknown} attributes - 帖子属性
* @return Base
*/
interface Base {
insert: never;
attributes: never;
}
/**
* @description 帖子结构化数据-联合类型
* @since Beta v0.3.4
* @interface Common
* @return Common
*/
type Common =
| Backup
| Divider
| Image
| Link
| LinkCard
| Mention
| Text
| Video
| VillaCard
| Vod;
/**
* @description 帖子结构化数据-其他类型
* @since Beta v0.3.4
* @property {string} describe - 描述
* @property {string[]} imgs - 图片链接
* @return Other
*/
interface Other {
describe: string;
imgs: string[];
[key: string]: unknown;
}
/**
* @description 帖子结构化数据-折叠文本
* @since Beta v0.3.4
* @interface Backup
* @extends Base
* @property {string} insert.backup_text - 折叠文本
* @property {string} insert.fold.title - 折叠标题
* @property {string} insert.fold.content - 折叠内容
* @return Backup
*/
interface Backup extends Base {
insert: {
backup_text: string;
fold: {
title: string;
content: string;
};
};
}
/**
* @description 帖子结构化数据-分割线
* @since Beta v0.3.4
* @interface Divider
* @extends Base
* @property {string} insert.divider - 分割线
* @return Divider
*/
interface Divider extends Base {
insert: {
divider: string;
};
}
/**
* @description 帖子结构化数据-图片类型
* @since Beta v0.3.4
* @interface Image
* @extends Base
* @property {string} insert.image - 图片链接
* @property {number} attributes.width - 图片宽度
* @property {number} attributes.height - 图片高度
* @property {number} [attributes.size] - 图片大小
* @property {string} [attributes.ext] - 图片格式
* @return Image
*/
interface Image extends Base {
insert: {
image: string;
};
attributes?: {
width: number;
height: number;
size: number | undefined;
ext: string | undefined;
};
}
/**
* @description 帖子结构化数据-文本链接
* @since Beta v0.3.4
* @interface Link
* @extends Base
* @property {string} insert - 帖子内容
* @property {string} attributes.link - 链接
* @return Link
*/
interface Link extends Base {
insert: string;
attributes: {
link: string;
};
}
/**
* @description 帖子结构化数据-链接卡片
* @since Beta v0.3.4
* @interface LinkCard
* @extends Base
* @property {number} insert.link_card.link_type - 链接类型
* @property {string} insert.link_card.origin_url - 原始链接
* @property {string} insert.link_card.landing_url - 落地页链接
* @property {string} insert.link_card.cover - 封面
* @property {string} insert.link_card.title - 标题
* @property {string} insert.link_card.card_id - 卡片ID
* @property {number} insert.link_card.card_status - 卡片状态
* @property {string} insert.link_card.market_price - 市场价
* @property {string} insert.link_card.price - 价格
* @property {string} insert.link_card.button_text - 按钮文本
* @property {number} insert.link_card.landing_url_type - 落地页类型
* @return LinkCard
*/
interface LinkCard extends Base {
insert: {
link_card: {
link_type: number;
origin_url: string;
landing_url: string;
cover: string;
title: string;
card_id: string;
card_status: number;
market_price: string;
price?: string;
button_text?: string;
landing_url_type: number;
};
};
}
/**
* @description 帖子结构化数据-抽奖
* @since Beta v0.3.4
* @interface Lottery
* @extends Base
* @property {"[抽奖]"} insert.backup_text - 抽奖文本
* @property {string} insert.lottery.id - 抽奖ID
* @property {string} insert.lottery.toast - 抽奖提示
* @return Lottery
*/
interface Lottery extends Base {
insert: {
backup_text: "[抽奖]";
lottery: {
id: string;
toast: string;
};
};
}
/**
* @description 帖子结构化数据-提及用户
* @since Beta v0.3.4
* @interface Mention
* @extends Base
* @property {string} insert.mention.uid - 用户ID
* @property {string} insert.mention.nickname - 用户昵称
* @return Mention
*/
interface Mention extends Base {
insert: {
mention: {
uid: string;
nickname: string;
};
};
}
/**
* @description 帖子结构化数据-文本类型
* @since Beta v0.3.4
* @interface Text
* @extends Base
* @property {string} insert - 帖子内容
* @property {boolean} [attributes.bold] - 是否加粗
* @property {string} [attributes.color] - 文本颜色
* @property {string} [attributes.link] - 链接
* @return Text
*/
interface Text extends Base {
insert: string;
attributes?: {
bold?: boolean;
color?: string;
align?: string;
};
}
/**
* @description 帖子结构化数据-视频类型-站外视频
* @since Beta v0.3.4
* @interface Video
* @extends Base
* @property {string} insert.video - 视频链接
* @return Video
*/
interface Video extends Base {
insert: {
video: string;
};
}
/**
* @description 帖子结构化数据-大别野卡片
* @since Beta v0.3.4
* @interface VillaCard
* @extends Base
* @property {string} insert.villa_card.villa_id - 别墅ID
* @property {string} insert.villa_card.villa_name - 别墅名称
* @property {string} insert.villa_card.villa_avatar_url - 别墅头像
* @property {string} insert.villa_card.villa_cover - 别墅封面
* @property {string} insert.villa_card.owner_uid - 别墅主人ID
* @property {string} insert.villa_card.owner_nickname - 别墅主人昵称
* @property {string} insert.villa_card.owner_avatar_url - 别墅主人头像
* @property {string} insert.villa_card.villa_introduce - 别墅介绍
* @property {string[]} insert.villa_card.tag_list - 别墅标签
* @property {string} insert.villa_card.villa_member_num - 别墅成员数量
* @return VillaCard
*/
interface VillaCard extends Base {
insert: {
villa_card: {
villa_id: string;
villa_name: string;
villa_avatar_url: string;
villa_cover: string;
owner_uid: string;
owner_nickname: string;
owner_avatar_url: string;
villa_introduce: string;
tag_list: string[];
villa_member_num: string;
};
};
}
/**
* @description 帖子结构化数据-视频类型-站内视频
* @since Beta v0.3.4
* @interface Vod
* @extends Base
* @property {number} insert.vod.id - 视频ID
* @property {number} insert.vod.duration - 视频时长
* @property {string} insert.vod.cover - 视频封面
* @property {Array} insert.vod.resolutions - 视频分辨率
* @property {string} insert.vod.resolutions.url - 视频链接
* @property {string} insert.vod.resolutions.definition - 视频清晰度
* @property {number} insert.vod.resolutions.height - 视频高度
* @property {number} insert.vod.resolutions.width - 视频宽度
* @property {number} insert.vod.resolutions.bitrate - 视频码率
* @property {number} insert.vod.resolutions.size - 视频大小
* @property {string} insert.vod.resolutions.format - 视频格式
* @property {string} insert.vod.resolutions.label - 视频标签
* @property {number} insert.vod.view_num - 观看次数
* @property {number} insert.vod.transcode_status - 转码状态
* @property {number} insert.vod.review_status - 审核状态
* @return Vod
*/
interface Vod extends Base {
insert: {
vod: {
id: number;
duration: number;
cover: string;
resolutions: Array<{
url: string;
definition: string;
height: number;
width: number;
bitrate: number;
size: number;
format: string;
label: string;
}>;
view_num: number;
transcode_status: number;
review_status: number;
};
};
}
}

View File

@@ -1,15 +1,16 @@
/** /**
* @file plugins Mys utils parsePost.ts * @file plugins Mys utils parsePost.ts
* @description 用于解析Mys数据的工具 * @description 用于解析Mys数据的工具
* @since Beta v0.3.3 * @since Beta v0.3.4
*/ */
import * as colorConvert from "color-convert"; import * as colorConvert from "color-convert";
import type { KEYWORD } from "color-convert/conversions";
import { score } from "wcag-color"; import { score } from "wcag-color";
/** /**
* @description 给定两个16进制颜色值确认两者是否相近 * @description 给定两个16进制颜色值确认两者是否相近
* @since Beta v0.3.0 * @since Beta v0.3.4
* @param {string} colorBg 背景颜色 * @param {string} colorBg 背景颜色
* @param {string} colorFg 前景颜色 * @param {string} colorFg 前景颜色
* @returns {boolean} 是否相近 * @returns {boolean} 是否相近
@@ -17,9 +18,9 @@ import { score } from "wcag-color";
function isColorSimilar(colorBg: string, colorFg: string): boolean { function isColorSimilar(colorBg: string, colorFg: string): boolean {
let hexBg, hexFg; let hexBg, hexFg;
if (colorBg.startsWith("#")) hexBg = colorBg; if (colorBg.startsWith("#")) hexBg = colorBg;
else hexBg = colorConvert.keyword.hex(colorBg); else hexBg = colorConvert.keyword.hex(<KEYWORD>colorBg);
if (colorFg.startsWith("#")) hexFg = colorFg; if (colorFg.startsWith("#")) hexFg = colorFg;
else hexFg = colorConvert.keyword.hex(colorFg); else hexFg = colorConvert.keyword.hex(<KEYWORD>colorFg);
const contrast = score(hexFg, hexBg); const contrast = score(hexFg, hexBg);
return contrast === "Fail"; return contrast === "Fail";
} }
@@ -72,16 +73,16 @@ function getVodTime(duration: number): string {
/** /**
* @description 解析用户帖子,将其转换为 StructContent * @description 解析用户帖子,将其转换为 StructContent
* @since Alpha v0.1.2 * @since Beta v0.3.4
* @see PostContent * @see PostContent
* @param {string} content 帖子内容 * @param {string} content 帖子内容
* @returns {string} 解析后的内容 * @returns {string} 解析后的内容
*/ */
function parseContent(content: string): string { function parseContent(content: string): string {
const data = JSON.parse(content); const data: TGApp.Plugins.Mys.SctPost.Other = JSON.parse(content);
const result: TGApp.Plugins.Mys.Post.StructuredContent[] = []; const result: TGApp.Plugins.Mys.SctPost.Common[] = [];
// 遍历 data 属性,值 const keys = Object.keys(data);
Object.keys(data).forEach((key) => { keys.forEach((key) => {
switch (key) { switch (key) {
case "describe": case "describe":
result.push({ result.push({
@@ -89,19 +90,20 @@ function parseContent(content: string): string {
}); });
break; break;
case "imgs": case "imgs":
for (const image of data.imgs) { data.imgs.forEach((item) => {
result.push({ result.push({
insert: { insert: {
image, image: item,
}, },
}); });
} });
break; break;
default: default:
// 如果是其他属性,就直接插入 console.warn(`[MysPostParser] Unknown key: ${key}`);
result.push({ result.push({
insert: JSON.stringify(data[key]), insert: JSON.stringify(data[key]),
}); });
break;
} }
}); });
return JSON.stringify(result); return JSON.stringify(result);
@@ -126,7 +128,7 @@ function parsePost(post: TGApp.Plugins.Mys.Post.FullData): string {
parserData = post.post.structured_content; parserData = post.post.structured_content;
} }
} }
const jsonData: TGApp.Plugins.Mys.Post.StructuredContent[] = JSON.parse(parserData); const jsonData: TGApp.Plugins.Mys.SctPost.Common[] = JSON.parse(parserData);
const doc = document.createElement("div"); const doc = document.createElement("div");
jsonData.forEach((item: any) => { jsonData.forEach((item: any) => {
const parsed = transferParser(item); const parsed = transferParser(item);
@@ -138,42 +140,39 @@ function parsePost(post: TGApp.Plugins.Mys.Post.FullData): string {
/** /**
* @description 解析中转 * @description 解析中转
* @since Beta v0.3.3 * @since Beta v0.3.3
* @param {TGApp.Plugins.Mys.Post.StructuredContent} data Mys数据 * @param {TGApp.Plugins.Mys.SctPost.Common} data Mys数据
* @returns {HTMLDivElement | HTMLSpanElement} 解析后的中转 * @returns {HTMLDivElement | HTMLSpanElement} 解析后的中转
*/ */
function transferParser( function transferParser(data: TGApp.Plugins.Mys.SctPost.Common): HTMLDivElement | HTMLSpanElement {
data: TGApp.Plugins.Mys.Post.StructuredContent,
): HTMLDivElement | HTMLSpanElement {
if (typeof data.insert === "string") { if (typeof data.insert === "string") {
return parseText(data); return parseText(<TGApp.Plugins.Mys.SctPost.Text | TGApp.Plugins.Mys.SctPost.Link>data);
} else if (data.insert.image) { } else if ("image" in data.insert) {
return parseImage(data); return parseImage(<TGApp.Plugins.Mys.SctPost.Image>data);
} else if (data.insert.vod != null) { } else if ("vod" in data.insert) {
return parseVideo(data); return parseVideo(<TGApp.Plugins.Mys.SctPost.Vod>data);
} else if (data.insert.video) { } else if ("video" in data.insert) {
return parseVideo(data); return parseVideo(<TGApp.Plugins.Mys.SctPost.Video>data);
} else if (data.insert.backup_text) { } else if ("backup_text" in data.insert) {
return parseBackup(data); return parseBackup(<TGApp.Plugins.Mys.SctPost.Backup>data);
} else if (data.insert.link_card != null) { } else if ("link_card" in data.insert) {
return parseLinkCard(data); return parseLinkCard(<TGApp.Plugins.Mys.SctPost.LinkCard>data);
} else if (data.insert.divider) { } else if ("divider" in data.insert) {
return parseDivider(data); return parseDivider(<TGApp.Plugins.Mys.SctPost.Divider>data);
} else if (data.insert.mention != null) { } else if ("mention" in data.insert) {
return parseMention(data); return parseMention(<TGApp.Plugins.Mys.SctPost.Mention>data);
} else if (data.insert.villa_card != null) { } else if ("villa_card" in data.insert) {
return parseVillaCard(data); return parseVillaCard(<TGApp.Plugins.Mys.SctPost.VillaCard>data);
} else {
return parseUnknown(data);
} }
return parseUnknown(<TGApp.Plugins.Mys.SctPost.Base>data);
} }
/** /**
* @description 解析未知数据 * @description 解析未知数据
* @since Beta v0.3.3 * @since Beta v0.3.4
* @param {TGApp.Plugins.Mys.Post.StructuredContent} data Mys数据 * @param {TGApp.Plugins.Mys.SctPost.Base} data Mys数据
* @returns {HTMLDivElement} 解析后的未知数据 * @returns {HTMLDivElement} 解析后的未知数据
*/ */
function parseUnknown(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLDivElement { function parseUnknown(data: TGApp.Plugins.Mys.SctPost.Base): HTMLDivElement {
const div = document.createElement("div"); const div = document.createElement("div");
div.classList.add("mys-post-unknown"); div.classList.add("mys-post-unknown");
const code = document.createElement("code"); const code = document.createElement("code");
@@ -186,63 +185,43 @@ function parseUnknown(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLDivEl
/** /**
* @description 解析文本 * @description 解析文本
* @since Beta v0.3.0 * @since Beta v0.3.0
* @param {TGApp.Plugins.Mys.Post.StructuredContent} data Mys数据 * @param {TGApp.Plugins.Mys.SctPost.Text |TGApp.Plugins.Mys.SctPost.Link} data Mys数据
* @returns {HTMLSpanElement} 解析后的文本 * @returns {HTMLSpanElement} 解析后的文本
*/ */
function parseText(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLSpanElement { function parseText(
// 检查数据 data: TGApp.Plugins.Mys.SctPost.Text | TGApp.Plugins.Mys.SctPost.Link,
if (typeof data.insert !== "string") { ): HTMLSpanElement {
throw new Error(`[ParseText] data.insert is not a string: ${JSON.stringify(data)}`); if (data.attributes && "link" in data.attributes) {
return LinkTextParser(<TGApp.Plugins.Mys.SctPost.Link>data);
} }
// 创建文本
const text = document.createElement("span"); const text = document.createElement("span");
// 设置文本属性 if (data.attributes) {
if (data.attributes != null) {
if (data.attributes.bold) text.style.fontWeight = "bold"; if (data.attributes.bold) text.style.fontWeight = "bold";
if (data.attributes.color) { if (data.attributes.color) {
let colorGet = data.attributes.color; let colorGet = data.attributes.color;
// 如果 colorGet 在 darkColorList 中,就设置为对应的颜色
if (isColorSimilar("#1E1E1E", colorGet)) { if (isColorSimilar("#1E1E1E", colorGet)) {
colorGet = "var(--app-page-content)"; colorGet = "var(--app-page-content)";
} }
text.style.color = colorGet; text.style.color = colorGet;
} }
if (data.attributes.link) {
return LinkTextParser(data);
}
} }
if (data.insert.startsWith("_(") && data.insert.endsWith(")")) { if (data.insert.startsWith("_(") && data.insert.endsWith(")")) {
return emojiParser(data); return emojiParser(<TGApp.Plugins.Mys.SctPost.Text>data);
} }
// 添加 class
text.classList.add("mys-post-span"); text.classList.add("mys-post-span");
// 设置 span 内容
text.innerText = data.insert; text.innerText = data.insert;
// 返回文本
return text; return text;
} }
/** /**
* @description 解析链接 * @description 解析链接
* @since Beta v0.3.0 * @since Beta v0.3.4
* @param {TGApp.Plugins.Mys.Post.StructuredContent} data Mys数据 * @param {TGApp.Plugins.Mys.SctPost.Link} data Mys数据
* @returns {HTMLSpanElement} 解析后的链接 * @returns {HTMLSpanElement} 解析后的链接
*/ */
function LinkTextParser(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLSpanElement { function LinkTextParser(data: TGApp.Plugins.Mys.SctPost.Link): HTMLSpanElement {
// 检查数据
if (typeof data.insert !== "string") {
throw new Error(`[LinkTextParser] data.insert is not a string: ${JSON.stringify(data)}`);
}
if (data.attributes == null) {
throw new Error("[LinkTextParser] data.attributes is not defined");
}
if (!data.attributes.link) {
throw new Error("[LinkTextParser] data.attributes.link is not defined");
}
// 创建图标
const icon = document.createElement("i"); const icon = document.createElement("i");
icon.classList.add("mdi", "mdi-link-variant"); icon.classList.add("mdi", "mdi-link-variant");
// 创建链接
const link = document.createElement("a"); const link = document.createElement("a");
const linkUrl = data.attributes.link; const linkUrl = data.attributes.link;
link.classList.add("mys-post-link"); link.classList.add("mys-post-link");
@@ -255,114 +234,72 @@ function LinkTextParser(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLSpa
link.target = "view_window"; link.target = "view_window";
} }
link.innerText = data.insert; link.innerText = data.insert;
// 插入图标作为链接前缀
link.prepend(icon); link.prepend(icon);
// 返回链接
return link; return link;
} }
/** /**
* @description 解析分割线 * @description 解析分割线
* @since Beta v0.3.0 * @since Beta v0.3.4
* @param {TGApp.Plugins.Mys.Post.StructuredContent} data Mys数据 * @param {TGApp.Plugins.Mys.SctPost.Divider} data Mys数据
* @returns {HTMLDivElement} 解析后的分割线 * @returns {HTMLDivElement} 解析后的分割线
*/ */
function parseDivider(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLDivElement { function parseDivider(data: TGApp.Plugins.Mys.SctPost.Divider): HTMLDivElement {
// 数据检查
if (typeof data.insert === "string") {
throw new Error(`[ParseDivider] data.insert is a string: ${data.insert}`);
}
if (!data.insert.divider) {
throw new Error("[ParseDivider] data.insert.divider is not defined");
}
// 创建分割线
const div = document.createElement("div"); const div = document.createElement("div");
div.classList.add("mys-post-divider"); div.classList.add("mys-post-divider");
// 创建 img
const img = document.createElement("img"); const img = document.createElement("img");
if (data.insert.divider === "line_1") { const dividerList = ["line_1", "line_2", "line_3", "line_4"];
img.src = "/source/post/divider_line_1.webp"; if (!dividerList.includes(data.insert.divider)) {
} else if (data.insert.divider === "line_2") {
img.src = "/source/post/divider_line_2.webp";
} else if (data.insert.divider === "line_3") {
img.src = "/source/post/divider_line_3.webp";
} else if (data.insert.divider === "line_4") {
img.src = "/source/post/divider_line_4.webp";
} else {
console.error("Unknown divider type", data); console.error("Unknown divider type", data);
return parseUnknown(data); return parseUnknown(<TGApp.Plugins.Mys.SctPost.Base>data);
} }
// 插入 img img.src = `/source/post/divider_${data.insert.divider}.webp`;
div.appendChild(img); div.appendChild(img);
// 返回分割线
return div; return div;
} }
/** /**
* @description 解析图片 * @description 解析图片
* @since Beta v0.3.0 * @since Beta v0.3.4
* @param {TGApp.Plugins.Mys.Post.StructuredContent} data Mys数据 * @param {TGApp.Plugins.Mys.SctPost.Image} data Mys数据
* @returns {HTMLDivElement} 解析后的图片 * @returns {HTMLDivElement} 解析后的图片
*/ */
function parseImage(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLDivElement { function parseImage(data: TGApp.Plugins.Mys.SctPost.Image): HTMLDivElement {
// 检查数据
if (typeof data.insert === "string") {
throw new Error(`[ParseImage] data.insert is a string: ${data.insert}`);
}
if (!data.insert.image) {
throw new Error("[ParseImage] data.insert.image is not defined");
}
const div = document.createElement("div"); const div = document.createElement("div");
// 创建图片
const img = document.createElement("img"); const img = document.createElement("img");
img.src = data.insert.image; img.src = data.insert.image;
// 添加 class
img.classList.add("mys-post-img"); img.classList.add("mys-post-img");
// 插入图片
div.appendChild(img); div.appendChild(img);
// 添加 class
div.classList.add("mys-post-div"); div.classList.add("mys-post-div");
// 返回 div
return div; return div;
} }
/** /**
* @description 解析视频 * @description 解析视频
* @since Beta v0.3.3 * @since Beta v0.3.3
* @param {TGApp.Plugins.Mys.Post.StructuredContent} data Mys数据 * @todo 分开解析 Vod 和 Video
* @param {TGApp.Plugins.Mys.SctPost.Vod|TGApp.Plugins.Mys.SctPost.Video} data Mys数据
* @returns {HTMLDivElement} 解析后的视频 * @returns {HTMLDivElement} 解析后的视频
*/ */
function parseVideo(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLDivElement { function parseVideo(
// 检查数据 data: TGApp.Plugins.Mys.SctPost.Vod | TGApp.Plugins.Mys.SctPost.Video,
if (typeof data.insert === "string") { ): HTMLDivElement {
throw new Error(`[ParseVideo] data.insert is a string: ${data.insert}`);
}
if (data.insert.vod == null && !data.insert.video) {
throw new Error("[ParseVideo] data.insert.vod and data.insert.video is not defined");
}
// 创建 div
const div = document.createElement("div"); const div = document.createElement("div");
div.classList.add("mys-post-div"); div.classList.add("mys-post-div");
if (data.insert.vod != null) { if ("vod" in data.insert) {
// 创建视频
const video = document.createElement("video"); const video = document.createElement("video");
video.classList.add("mys-post-vod"); video.classList.add("mys-post-vod");
// 获取 resolutions中size最大的视频
const resolution = data.insert.vod.resolutions.reduce((prev: any, curr: any) => { const resolution = data.insert.vod.resolutions.reduce((prev: any, curr: any) => {
if (prev.size > curr.size) return prev; if (prev.size > curr.size) return prev;
return curr; return curr;
}); });
video.poster = data.insert.vod.cover; // 设置封面 video.poster = data.insert.vod.cover;
video.controls = true; // 设置 controls video.controls = true;
// 添加 source
const source = document.createElement("source"); const source = document.createElement("source");
source.src = resolution.url; source.src = resolution.url;
source.type = resolution.format === ".mp4" ? "video/mp4" : "video/webm"; source.type = resolution.format === ".mp4" ? "video/mp4" : "video/webm";
// 插入 source
video.appendChild(source); video.appendChild(source);
// 插入 video
div.appendChild(video); div.appendChild(video);
// 创建视频封面图
const coverDiv = document.createElement("div"); const coverDiv = document.createElement("div");
const cover = document.createElement("img"); const cover = document.createElement("img");
cover.classList.add("mys-post-vod-cover"); cover.classList.add("mys-post-vod-cover");
@@ -378,15 +315,12 @@ function parseVideo(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLDivElem
coverDiv.appendChild(playTime); coverDiv.appendChild(playTime);
coverDiv.classList.add("mys-post-vod-cover-div"); coverDiv.classList.add("mys-post-vod-cover-div");
div.appendChild(coverDiv); div.appendChild(coverDiv);
} else if (data.insert.video) { } else {
// 创建 iframe
const video = document.createElement("iframe"); const video = document.createElement("iframe");
video.classList.add("mys-post-iframe"); video.classList.add("mys-post-iframe");
// 设置 iframe 属性
video.src = data.insert.video; video.src = data.insert.video;
video.allowFullscreen = true; video.allowFullscreen = true;
video.sandbox.add("allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts"); video.sandbox.add("allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts");
// 插入 video
div.appendChild(video); div.appendChild(video);
} }
return div; return div;
@@ -394,40 +328,27 @@ function parseVideo(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLDivElem
/** /**
* @description 解析折叠内容 * @description 解析折叠内容
* @since Beta v0.3.0 * @since Beta v0.3.4
* @param {TGApp.Plugins.Mys.Post.StructuredContent} data Mys数据 * @param {TGApp.Plugins.Mys.SctPost.Backup} data Mys数据
* @returns {HTMLDivElement} 解析后的折叠内容 * @returns {HTMLDivElement} 解析后的折叠内容
*/ */
function parseBackup(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLDivElement { function parseBackup(
// 检查数据 data: TGApp.Plugins.Mys.SctPost.Backup | TGApp.Plugins.Mys.SctPost.Lottery,
if (typeof data.insert === "string") { ): HTMLDivElement {
throw new Error(`[ParseBackup] data.insert is a string: ${data.insert}`); if ("lottery" in data.insert) {
return LotteryParser(<TGApp.Plugins.Mys.SctPost.Lottery>data);
} }
if (data.insert.backup_text === "[抽奖]") { const titleJson: TGApp.Plugins.Mys.SctPost.Base[] = JSON.parse(data.insert.fold.title);
return LotteryParser(data); const contentJson: TGApp.Plugins.Mys.SctPost.Base[] = JSON.parse(data.insert.fold.content);
}
if (data.insert.fold == null) {
throw new Error("[ParseBackup] data.insert.fold is not defined");
}
// 转换
const titleJson: TGApp.Plugins.Mys.Post.StructuredContent[] = JSON.parse(data.insert.fold.title);
const contentJson: TGApp.Plugins.Mys.Post.StructuredContent[] = JSON.parse(
data.insert.fold.content,
);
// 创建 div
const div = document.createElement("div"); const div = document.createElement("div");
div.classList.add("mys-post-div"); div.classList.add("mys-post-div");
// 创建折叠内容
const details = document.createElement("details"); const details = document.createElement("details");
details.classList.add("mys-post-details"); details.classList.add("mys-post-details");
// 创建标题
const title = document.createElement("summary"); const title = document.createElement("summary");
// 解析标题
titleJson.forEach((item) => { titleJson.forEach((item) => {
const parsed = transferParser(item); const parsed = transferParser(item);
title.appendChild(parsed); title.appendChild(parsed);
}); });
// 创建内容
const content = document.createElement("div"); const content = document.createElement("div");
contentJson.forEach((item) => { contentJson.forEach((item) => {
const parsed = transferParser(item); const parsed = transferParser(item);
@@ -436,82 +357,47 @@ function parseBackup(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLDivEle
details.appendChild(title); details.appendChild(title);
details.appendChild(content); details.appendChild(content);
div.appendChild(details); div.appendChild(details);
// 返回 div
return div; return div;
} }
/** /**
* @description 解析抽奖 * @description 解析抽奖
* @since Beta v0.3.0 * @since Beta v0.3.4
* @param {TGApp.Plugins.Mys.Post.StructuredContent} data Mys数据 * @param {TGApp.Plugins.Mys.SctPost.Lottery} data Mys数据
* @returns {HTMLDivElement} 解析后的抽奖 * @returns {HTMLDivElement} 解析后的抽奖
*/ */
function LotteryParser(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLDivElement { function LotteryParser(data: TGApp.Plugins.Mys.SctPost.Lottery): HTMLDivElement {
// 检查数据
if (typeof data.insert === "string") {
throw new Error(`[LotteryParser] data.insert is a string: ${data.insert}`);
}
if (!data.insert.backup_text) {
throw new Error("[LotteryParser] data.insert.backup_text is not defined");
}
if (data.insert.backup_text !== "[抽奖]") {
throw new Error("[LotteryParser] data.insert.backup_text is not [抽奖]");
}
if (data.insert.lottery == null) {
throw new Error("[LotteryParser] data.insert.lottery is not defined");
}
// 创建 div
const div = document.createElement("div"); const div = document.createElement("div");
// 创建图标
const icon = document.createElement("i"); const icon = document.createElement("i");
icon.classList.add("mdi", "mdi-gift"); icon.classList.add("mdi", "mdi-gift");
// 创建标题
const title = document.createElement("a"); const title = document.createElement("a");
title.classList.add("mys-post-link"); title.classList.add("mys-post-link");
title.href = `/lottery/${data.insert.lottery.id}`; title.href = `/lottery/${data.insert.lottery.id}`;
title.innerText = data.insert.lottery.toast; title.innerText = data.insert.lottery.toast;
// 插入图标
title.prepend(icon); title.prepend(icon);
// 插入标题
div.appendChild(title); div.appendChild(title);
// 返回 div
return div; return div;
} }
/** /**
* @description 解析链接卡片 * @description 解析链接卡片
* @since Beta v0.3.0 * @since Beta v0.3.4
* @param {TGApp.Plugins.Mys.Post.StructuredContent} data Mys数据 * @param {TGApp.Plugins.Mys.SctPost.LinkCard} data Mys数据
* @returns {HTMLDivElement} 解析后的链接卡片 * @returns {HTMLDivElement} 解析后的链接卡片
*/ */
function parseLinkCard(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLDivElement { function parseLinkCard(data: TGApp.Plugins.Mys.SctPost.LinkCard): HTMLDivElement {
// 检查数据
if (typeof data.insert === "string") {
throw new Error(`[ParseLinkCard] data.insert is a string: ${data.insert}`);
}
if (data.insert.link_card == null) {
throw new Error("[ParseLinkCard] data.insert.link_card is not defined");
}
// 创建 div
const div = document.createElement("div"); const div = document.createElement("div");
// 创建 cover
const cover = document.createElement("div"); const cover = document.createElement("div");
cover.classList.add("mys-post-link-card-cover"); cover.classList.add("mys-post-link-card-cover");
// 创建 img
const img = document.createElement("img"); const img = document.createElement("img");
img.src = data.insert.link_card.cover; img.src = data.insert.link_card.cover;
// 插入 img
cover.appendChild(img); cover.appendChild(img);
// 插入 cover
div.appendChild(cover); div.appendChild(cover);
// 创建 content
const content = document.createElement("div"); const content = document.createElement("div");
content.classList.add("mys-post-link-card-content"); content.classList.add("mys-post-link-card-content");
// 创建标题
const title = document.createElement("div"); const title = document.createElement("div");
title.classList.add("mys-post-link-card-title"); title.classList.add("mys-post-link-card-title");
title.innerHTML = data.insert.link_card.title; title.innerHTML = data.insert.link_card.title;
// 插入 title
content.appendChild(title); content.appendChild(title);
if (data.insert.link_card.price) { if (data.insert.link_card.price) {
const price = document.createElement("div"); const price = document.createElement("div");
@@ -519,10 +405,9 @@ function parseLinkCard(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLDivE
price.innerHTML = data.insert.link_card.price; price.innerHTML = data.insert.link_card.price;
content.appendChild(price); content.appendChild(price);
} }
// 创建 button
const button = document.createElement("a"); const button = document.createElement("a");
button.classList.add("mys-post-link-card-btn"); button.classList.add("mys-post-link-card-btn");
button.innerHTML = (data.insert.link_card.button_text || "详情") + " >"; button.innerHTML = (data.insert.link_card.button_text ?? "详情") + " >";
const linkUrl = data.insert.link_card.origin_url; const linkUrl = data.insert.link_card.origin_url;
if (isMysPost(linkUrl)) { if (isMysPost(linkUrl)) {
const postId = getPostId(linkUrl); const postId = getPostId(linkUrl);
@@ -532,54 +417,37 @@ function parseLinkCard(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLDivE
button.href = linkUrl; button.href = linkUrl;
button.target = "view_window"; button.target = "view_window";
} }
// 插入 button
content.appendChild(button); content.appendChild(button);
// 插入 content
div.appendChild(content); div.appendChild(content);
// 添加 class
div.classList.add("mys-post-link-card"); div.classList.add("mys-post-link-card");
return div; return div;
} }
/** /**
* @description 解析 Mention * @description 解析 Mention
* @since Beta v0.3.0 * @since Beta v0.3.4
* @param {TGApp.Plugins.Mys.Post.StructuredContent} data Mys数据 * @param {TGApp.Plugins.Mys.SctPost.Mention} data Mys数据
* @returns {HTMLAnchorElement} 解析后的 Mention * @returns {HTMLAnchorElement} 解析后的 Mention
*/ */
function parseMention(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLAnchorElement { function parseMention(data: TGApp.Plugins.Mys.SctPost.Mention): HTMLAnchorElement {
// 检查数据
if (typeof data.insert === "string") {
throw new Error(`[ParseMention] data.insert is a string: ${data.insert}`);
}
if (data.insert.mention == null) {
throw new Error("[ParseMention] data.insert.mention is not defined");
}
// 创建图标
const icon = document.createElement("i"); const icon = document.createElement("i");
icon.classList.add("mdi", "mdi-account-circle-outline"); icon.classList.add("mdi", "mdi-account-circle-outline");
// 创建链接
const link = document.createElement("a"); const link = document.createElement("a");
link.classList.add("mys-post-link"); link.classList.add("mys-post-link");
link.href = `https://www.miyoushe.com/ys/accountCenter/postList?id=${data.insert.mention.uid}`; link.href = `https://www.miyoushe.com/ys/accountCenter/postList?id=${data.insert.mention.uid}`;
link.target = "_blank"; link.target = "_blank";
link.innerText = data.insert.mention.nickname; link.innerText = data.insert.mention.nickname;
// 插入图标
link.prepend(icon); link.prepend(icon);
return link; return link;
} }
/** /**
* @description 解析 Emoji * @description 解析 Emoji
* @since Beta v0.3.2 * @since Beta v0.3.4
* @param {TGApp.Plugins.Mys.Post.StructuredContent} data Mys数据 * @param {TGApp.Plugins.Mys.SctPost.Text} data Mys数据
* @returns {HTMLSpanElement} 解析后的 Emoji * @returns {HTMLSpanElement} 解析后的 Emoji
*/ */
function emojiParser(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLImageElement { function emojiParser(data: TGApp.Plugins.Mys.SctPost.Text): HTMLImageElement {
// 检查数据
if (typeof data.insert !== "string") {
throw new Error(`[EmojiParser] data.insert is not a string: ${JSON.stringify(data)}`);
}
const emojis = localStorage.getItem("emojis"); const emojis = localStorage.getItem("emojis");
if (!emojis) { if (!emojis) {
throw new Error("[EmojiParser] emojis is not defined"); throw new Error("[EmojiParser] emojis is not defined");
@@ -590,29 +458,21 @@ function emojiParser(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLImageE
if (!emoji) { if (!emoji) {
throw new Error(`[EmojiParser] emoji is not defined: ${emojiName}`); throw new Error(`[EmojiParser] emoji is not defined: ${emojiName}`);
} }
// 创建图片
const img = document.createElement("img"); const img = document.createElement("img");
img.classList.add("mys-post-emoji"); img.classList.add("mys-post-emoji");
img.src = emoji; img.src = emoji;
img.alt = emojiName; img.alt = emojiName;
img.title = emojiName; img.title = emojiName;
// 获取图片地址
return img; return img;
} }
/** /**
* @description 解析大别野房间卡片 * @description 解析大别野房间卡片
* @since Beta v0.3.3 * @since Beta v0.3.4
* @param {TGApp.Plugins.Mys.Post.StructuredContent} data Mys数据 * @param {TGApp.Plugins.Mys.SctPost.VillaCard} data Mys数据
* @returns {HTMLDivElement} 解析后的大别野房间卡片 * @returns {HTMLDivElement} 解析后的大别野房间卡片
*/ */
function parseVillaCard(data: TGApp.Plugins.Mys.Post.StructuredContent): HTMLDivElement { function parseVillaCard(data: TGApp.Plugins.Mys.SctPost.VillaCard): HTMLDivElement {
if (typeof data.insert === "string") {
throw new Error(`[parseVillaCard] data.insert is a string: ${data.insert}`);
}
if (data.insert.villa_card == null) {
throw new Error("[parseVillaCard] data.insert.villa_card is not defined");
}
const div = document.createElement("div"); const div = document.createElement("div");
div.classList.add("mys-post-div"); div.classList.add("mys-post-div");
const villaCard = document.createElement("div"); const villaCard = document.createElement("div");