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