mirror of
https://github.com/kirameki-cafe/Yumi.git
synced 2026-09-13 18:59:19 +00:00
Support localization
This commit is contained in:
+170
-115
@@ -1,92 +1,98 @@
|
||||
import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule";
|
||||
import {
|
||||
Message,
|
||||
CommandInteraction,
|
||||
Interaction,
|
||||
VoiceChannel,
|
||||
MessageActionRow,
|
||||
MessageButton,
|
||||
SelectMenuInteraction,
|
||||
ButtonInteraction
|
||||
} from 'discord.js';
|
||||
import { I18n } from 'i18n';
|
||||
|
||||
import { Message, CommandInteraction, Interaction, VoiceChannel, MessageActionRow, MessageButton, SelectMenuInteraction, ButtonInteraction } from "discord.js";
|
||||
import { makeErrorEmbed, makeSuccessEmbed, sendHybridInteractionMessageResponse, makeInfoEmbed } from "../../utils/DiscordMessage";
|
||||
import DiscordProvider from "../../providers/Discord";
|
||||
import DiscordMusicPlayer, { ValidTracks } from "../../providers/DiscordMusicPlayer";
|
||||
import { joinVoiceChannelProcedure } from "./Join";
|
||||
import { joinVoiceChannelProcedure } from './Join';
|
||||
|
||||
import DiscordProvider from '../../providers/Discord';
|
||||
import DiscordMusicPlayer, { ValidTracks } from '../../providers/DiscordMusicPlayer';
|
||||
import Locale from '../../services/Locale';
|
||||
|
||||
import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule';
|
||||
import {
|
||||
makeErrorEmbed,
|
||||
makeSuccessEmbed,
|
||||
sendHybridInteractionMessageResponse,
|
||||
makeInfoEmbed
|
||||
} from '../../utils/DiscordMessage';
|
||||
|
||||
const EMBEDS = {
|
||||
PLAY_INFO: (data: Message | Interaction) => {
|
||||
PLAY_INFO: (data: HybridInteractionMessage, locale: I18n) => {
|
||||
return makeInfoEmbed({
|
||||
title: 'Play',
|
||||
description: `Play a song`,
|
||||
title: locale.__('musicplayer_play.title'),
|
||||
description: locale.__('musicplayer_play.info'),
|
||||
fields: [
|
||||
{
|
||||
name: 'Arguments',
|
||||
value: '``Search query or Link``'
|
||||
name: locale.__('common.available_args'),
|
||||
value: locale.__('musicplayer_play.valid_args'),
|
||||
}
|
||||
],
|
||||
user: (data instanceof Interaction) ? data.user : data.author
|
||||
user: data.getUser()
|
||||
});
|
||||
},
|
||||
YOUTUBE_ONLY: (data: Message | Interaction) => {
|
||||
return makeErrorEmbed({
|
||||
title: `Only youtube.com link is supported for now`,
|
||||
user: (data instanceof Interaction) ? data.user : data.author
|
||||
});
|
||||
},
|
||||
INVALID_LINK: (data: Message | Interaction) => {
|
||||
return makeErrorEmbed({
|
||||
title: `Invalid Link`,
|
||||
user: (data instanceof Interaction) ? data.user : data.author
|
||||
});
|
||||
},
|
||||
ADDED_QUEUE: (data: Message | Interaction, track: ValidTracks) => {
|
||||
ADDED_QUEUE: (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => {
|
||||
let embed = makeSuccessEmbed({
|
||||
title: `Song added to queue!`,
|
||||
description: `${track.title}`,
|
||||
user: (data instanceof Interaction) ? data.user : data.author
|
||||
title: locale.__('musicplayer_play.queue_added_song'),
|
||||
description: track.title,
|
||||
user: data.getUser()
|
||||
});
|
||||
|
||||
const highestResolutionThumbnail = track.thumbnails.reduce((prev, current) => (prev.height * prev.width > current.height * current.width) ? prev : current)
|
||||
const highestResolutionThumbnail = track.thumbnails.reduce((prev, current) =>
|
||||
prev.height * prev.width > current.height * current.width ? prev : current
|
||||
);
|
||||
|
||||
if (highestResolutionThumbnail) embed.setImage(highestResolutionThumbnail.url);
|
||||
|
||||
if(highestResolutionThumbnail)
|
||||
embed.setImage(highestResolutionThumbnail.url);
|
||||
|
||||
return embed;
|
||||
},
|
||||
ADDED_SONGS_QUEUE: (data: Message | Interaction, track: ValidTracks[]) => {
|
||||
ADDED_SONGS_QUEUE: (data: HybridInteractionMessage, locale: I18n, track: ValidTracks[]) => {
|
||||
let embed = makeSuccessEmbed({
|
||||
title: `Songs added to queue!`,
|
||||
description: `Added ${track.length} songs to queue`,
|
||||
user: (data instanceof Interaction) ? data.user : data.author
|
||||
title: locale.__('musicplayer_play.queue_added_songs'),
|
||||
description: locale.__('musicplayer_play.queue_added_songs_description', track.length.toString()),
|
||||
user: data.getUser()
|
||||
});
|
||||
return embed;
|
||||
},
|
||||
USER_NOT_IN_VOICECHANNEL: (data: Message | Interaction) => {
|
||||
USER_NOT_IN_VOICECHANNEL: (data: HybridInteractionMessage, locale: I18n) => {
|
||||
return makeErrorEmbed({
|
||||
title: `You need to be in the voice channel first!`,
|
||||
user: (data instanceof Interaction) ? data.user : data.author
|
||||
title: locale.__('musicplayer.not_in_voice'),
|
||||
user: data.getUser()
|
||||
});
|
||||
},
|
||||
USER_NOT_IN_SAME_VOICECHANNEL: (data: Message | Interaction) => {
|
||||
USER_NOT_IN_SAME_VOICECHANNEL: (data: HybridInteractionMessage, locale: I18n) => {
|
||||
return makeErrorEmbed({
|
||||
title: `You are not in the same voice channel!`,
|
||||
user: (data instanceof Interaction) ? data.user : data.author
|
||||
title: locale.__('musicplayer.different_voice_channel'),
|
||||
user: data.getUser()
|
||||
});
|
||||
},
|
||||
LOOKUP_ERROR: (data: Message | Interaction, error: Error) => {
|
||||
LOOKUP_ERROR: (data: HybridInteractionMessage, locale: I18n, error: Error) => {
|
||||
let errorMessage = '';
|
||||
|
||||
let errorMessage = "";
|
||||
|
||||
if(error.message === "While getting info from url\nPrivate video")
|
||||
errorMessage = "This is a private video";
|
||||
if (error.message === 'While getting info from url\nPrivate video') errorMessage = 'This is a private video';
|
||||
else
|
||||
errorMessage = error.message.replace("While getting info from url\n", "").replace("While getting info from url", "");
|
||||
errorMessage = error.message
|
||||
.replace('While getting info from url\n', '')
|
||||
.replace('While getting info from url', '');
|
||||
|
||||
return makeErrorEmbed({
|
||||
title: `Error while looking up the song`,
|
||||
description: `${errorMessage}`,
|
||||
user: (data instanceof Interaction) ? data.user : data.author
|
||||
title: locale.__('musicplayer_play.error'),
|
||||
description: errorMessage,
|
||||
user: data.getUser()
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
export default class Play extends DiscordModule {
|
||||
|
||||
public id = "Discord_MusicPlayer_Play";
|
||||
public commands = ["play", "p"];
|
||||
public commandInteractionName = "play";
|
||||
public id = 'Discord_MusicPlayer_Play';
|
||||
public commands = ['play', 'p'];
|
||||
public commandInteractionName = 'play';
|
||||
|
||||
async GuildOnModuleCommand(args: any, message: Message) {
|
||||
await this.run(new HybridInteractionMessage(message), args);
|
||||
@@ -105,6 +111,8 @@ export default class Play extends DiscordModule {
|
||||
if (!guild || !member || !user) return;
|
||||
if (!this.isJSONValid(interaction.customId)) return;
|
||||
|
||||
const locale = await Locale.getGuildLocale(guild.id);
|
||||
|
||||
let payload = JSON.parse(interaction.customId);
|
||||
|
||||
if (typeof payload.m === 'undefined' || typeof payload.a === 'undefined') return;
|
||||
@@ -118,7 +126,6 @@ export default class Play extends DiscordModule {
|
||||
*/
|
||||
|
||||
if (payload.m === 'MP_P' && payload.a === 'arp') {
|
||||
|
||||
if (typeof payload.d === 'undefined') return;
|
||||
|
||||
let data = payload.d.split('$');
|
||||
@@ -128,7 +135,7 @@ export default class Play extends DiscordModule {
|
||||
c: data[0],
|
||||
v: data[1],
|
||||
l: data[2]
|
||||
}
|
||||
};
|
||||
|
||||
await interaction.deferReply();
|
||||
|
||||
@@ -137,17 +144,24 @@ export default class Play extends DiscordModule {
|
||||
|
||||
if (!voiceChannel || !(voiceChannel instanceof VoiceChannel)) return;
|
||||
|
||||
|
||||
if (!member.voice.channel)
|
||||
return await sendHybridInteractionMessageResponse(hybridData, { embeds: [EMBEDS.USER_NOT_IN_VOICECHANNEL(hybridData.getRaw())] }, true);
|
||||
return await sendHybridInteractionMessageResponse(
|
||||
hybridData,
|
||||
{ embeds: [EMBEDS.USER_NOT_IN_VOICECHANNEL(hybridData, locale)] },
|
||||
true
|
||||
);
|
||||
|
||||
if (!DiscordMusicPlayer.isGuildInstanceExists(guild.id))
|
||||
await joinVoiceChannelProcedure(new HybridInteractionMessage(interaction) , null, voiceChannel);
|
||||
await joinVoiceChannelProcedure(new HybridInteractionMessage(interaction), null, voiceChannel);
|
||||
|
||||
let instance = DiscordMusicPlayer.getGuildInstance(guild.id);
|
||||
|
||||
if (instance!.voiceChannel.id !== member.voice.channel.id)
|
||||
return await sendHybridInteractionMessageResponse(hybridData, { embeds: [EMBEDS.USER_NOT_IN_SAME_VOICECHANNEL(hybridData.getRaw())] }, true);
|
||||
return await sendHybridInteractionMessageResponse(
|
||||
hybridData,
|
||||
{ embeds: [EMBEDS.USER_NOT_IN_SAME_VOICECHANNEL(hybridData, locale)] },
|
||||
true
|
||||
);
|
||||
|
||||
if (!instance!.isConnected()) {
|
||||
await joinVoiceChannelProcedure(new HybridInteractionMessage(interaction), instance!, voiceChannel);
|
||||
@@ -156,7 +170,9 @@ export default class Play extends DiscordModule {
|
||||
|
||||
if (!instance) return;
|
||||
|
||||
let playlist = await DiscordMusicPlayer.getYouTubeSongsInPlayList(`https://www.youtube.com/watch?v=${payload.d.v}&list=${payload.d.l}`);
|
||||
let playlist = await DiscordMusicPlayer.getYouTubeSongsInPlayList(
|
||||
`https://www.youtube.com/watch?v=${payload.d.v}&list=${payload.d.l}`
|
||||
);
|
||||
const songs = (await playlist.all_videos()).filter((song) => {
|
||||
return payload.d.v !== song.id;
|
||||
});
|
||||
@@ -165,15 +181,18 @@ export default class Play extends DiscordModule {
|
||||
instance.addTrackToQueue(song);
|
||||
}
|
||||
|
||||
if(hybridData.getSelectMenu().message instanceof Message)
|
||||
if (hybridData.getSelectMenu().message instanceof Message)
|
||||
await (hybridData.getSelectMenu().message as Message).edit({ components: [] });
|
||||
|
||||
return await sendHybridInteractionMessageResponse(hybridData, { embeds: [EMBEDS.ADDED_SONGS_QUEUE(hybridData.getRaw(), songs)] }, true);
|
||||
return await sendHybridInteractionMessageResponse(
|
||||
hybridData,
|
||||
{ embeds: [EMBEDS.ADDED_SONGS_QUEUE(hybridData, locale, songs)] },
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async GuildSelectMenuInteractionCreate(interaction: SelectMenuInteraction) {
|
||||
|
||||
const hybridData = new HybridInteractionMessage(interaction);
|
||||
const guild = hybridData.getGuild();
|
||||
const member = hybridData.getMember();
|
||||
@@ -182,6 +201,7 @@ export default class Play extends DiscordModule {
|
||||
if (!guild || !member || !user) return;
|
||||
if (!this.isJSONValid(interaction.customId)) return;
|
||||
|
||||
const locale = await Locale.getGuildLocale(guild.id);
|
||||
let payload = JSON.parse(interaction.customId);
|
||||
|
||||
/*
|
||||
@@ -194,7 +214,6 @@ export default class Play extends DiscordModule {
|
||||
if (typeof payload.m === 'undefined' || typeof payload.a === 'undefined') return;
|
||||
|
||||
if (payload.m === 'MP_SM' && payload.a === 'play') {
|
||||
|
||||
await interaction.deferReply();
|
||||
|
||||
const voiceChannel = DiscordProvider.client.guilds.cache.get(guild.id)?.channels.cache.get(payload.d.v);
|
||||
@@ -203,16 +222,23 @@ export default class Play extends DiscordModule {
|
||||
if (!voiceChannel || !(voiceChannel instanceof VoiceChannel)) return;
|
||||
|
||||
if (!member.voice.channel)
|
||||
return await sendHybridInteractionMessageResponse(hybridData, { embeds: [EMBEDS.USER_NOT_IN_VOICECHANNEL(interaction)] }, true);
|
||||
return await sendHybridInteractionMessageResponse(
|
||||
hybridData,
|
||||
{ embeds: [EMBEDS.USER_NOT_IN_VOICECHANNEL(new HybridInteractionMessage(interaction), locale)] },
|
||||
true
|
||||
);
|
||||
|
||||
if (!DiscordMusicPlayer.isGuildInstanceExists(guild.id))
|
||||
await joinVoiceChannelProcedure(new HybridInteractionMessage(interaction), null, voiceChannel);
|
||||
|
||||
|
||||
let instance = DiscordMusicPlayer.getGuildInstance(guild.id);
|
||||
|
||||
if (instance!.voiceChannel.id !== member.voice.channel.id)
|
||||
return await sendHybridInteractionMessageResponse(hybridData, { embeds: [EMBEDS.USER_NOT_IN_SAME_VOICECHANNEL(interaction)] }, true);
|
||||
return await sendHybridInteractionMessageResponse(
|
||||
hybridData,
|
||||
{ embeds: [EMBEDS.USER_NOT_IN_SAME_VOICECHANNEL(new HybridInteractionMessage(interaction), locale)] },
|
||||
true
|
||||
);
|
||||
|
||||
if (!instance!.isConnected()) {
|
||||
await joinVoiceChannelProcedure(new HybridInteractionMessage(interaction), instance!, voiceChannel);
|
||||
@@ -221,39 +247,52 @@ export default class Play extends DiscordModule {
|
||||
|
||||
if (!instance) return;
|
||||
|
||||
let result = await DiscordMusicPlayer.searchYouTubeByYouTubeLink(DiscordMusicPlayer.parseYouTubeLink(interaction.values[0])).catch((err) => {
|
||||
sendHybridInteractionMessageResponse(hybridData, { embeds: [EMBEDS.LOOKUP_ERROR(hybridData.getRaw(), err)] }, true);
|
||||
let result = await DiscordMusicPlayer.searchYouTubeByYouTubeLink(
|
||||
DiscordMusicPlayer.parseYouTubeLink(interaction.values[0])
|
||||
).catch((err) => {
|
||||
sendHybridInteractionMessageResponse(
|
||||
hybridData,
|
||||
{ embeds: [EMBEDS.LOOKUP_ERROR(hybridData, locale, err)] },
|
||||
true
|
||||
);
|
||||
return null;
|
||||
});
|
||||
if (!result) return;
|
||||
|
||||
instance.addTrackToQueue(result);
|
||||
return await sendHybridInteractionMessageResponse(hybridData, { embeds: [EMBEDS.ADDED_QUEUE(interaction, result)] }, true);
|
||||
return await sendHybridInteractionMessageResponse(
|
||||
hybridData,
|
||||
{ embeds: [EMBEDS.ADDED_QUEUE(new HybridInteractionMessage(interaction), locale, result)] },
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async run(data: HybridInteractionMessage, args: any) {
|
||||
|
||||
let query;
|
||||
const guild = data.getGuild();
|
||||
const channel = data.getChannel();
|
||||
const member = data.getMember();
|
||||
|
||||
if (!guild || !channel || !member) return;
|
||||
|
||||
const locale = await Locale.getGuildLocale(guild.id);
|
||||
|
||||
if (data.isMessage()) {
|
||||
if (args.length === 0)
|
||||
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.PLAY_INFO(data.getRaw())] });
|
||||
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.PLAY_INFO(data, locale)] });
|
||||
|
||||
query = args.join(' ');
|
||||
}
|
||||
else if (data.isSlashCommand()) {
|
||||
} else if (data.isSlashCommand()) {
|
||||
query = args.getSubcommand();
|
||||
}
|
||||
|
||||
if (!query || !guild || !channel || !member) return;
|
||||
if (!query) return;
|
||||
|
||||
if (!member.voice.channel)
|
||||
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.USER_NOT_IN_VOICECHANNEL(data.getRaw())] });
|
||||
return await sendHybridInteractionMessageResponse(data, {
|
||||
embeds: [EMBEDS.USER_NOT_IN_VOICECHANNEL(data, locale)]
|
||||
});
|
||||
|
||||
const voiceChannel = member.voice.channel;
|
||||
|
||||
@@ -264,7 +303,11 @@ export default class Play extends DiscordModule {
|
||||
let instance = DiscordMusicPlayer.getGuildInstance(guild.id);
|
||||
|
||||
if (instance!.voiceChannel.id !== member.voice.channel.id)
|
||||
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.USER_NOT_IN_SAME_VOICECHANNEL(data.getRaw())] }, true);
|
||||
return await sendHybridInteractionMessageResponse(
|
||||
data,
|
||||
{ embeds: [EMBEDS.USER_NOT_IN_SAME_VOICECHANNEL(data, locale)] },
|
||||
true
|
||||
);
|
||||
|
||||
if (!instance!.isConnected()) {
|
||||
await joinVoiceChannelProcedure(data, instance!, voiceChannel);
|
||||
@@ -276,18 +319,22 @@ export default class Play extends DiscordModule {
|
||||
if (DiscordMusicPlayer.isYouTubeLink(query)) {
|
||||
let linkData = DiscordMusicPlayer.parseYouTubeLink(query);
|
||||
|
||||
if (linkData.videoId === "" && linkData.list) {
|
||||
if (linkData.videoId === '' && linkData.list) {
|
||||
let playlist = await DiscordMusicPlayer.getYouTubeSongsInPlayList(query);
|
||||
const songs = await playlist.all_videos();
|
||||
|
||||
for (let song of songs) {
|
||||
instance.addTrackToQueue(song);
|
||||
}
|
||||
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.ADDED_SONGS_QUEUE(data.getRaw(), songs)] }, true);
|
||||
return await sendHybridInteractionMessageResponse(
|
||||
data,
|
||||
{ embeds: [EMBEDS.ADDED_SONGS_QUEUE(data, locale, songs)] },
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
let result = await DiscordMusicPlayer.searchYouTubeByYouTubeLink(linkData).catch((err) => {
|
||||
sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.LOOKUP_ERROR(data.getRaw(), err)] }, true);
|
||||
sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.LOOKUP_ERROR(data, locale, err)] }, true);
|
||||
return null;
|
||||
});
|
||||
|
||||
@@ -300,23 +347,28 @@ export default class Play extends DiscordModule {
|
||||
instance.addTrackToQueue(result);
|
||||
|
||||
if (linkData.list) {
|
||||
const row = new MessageActionRow()
|
||||
.addComponents(
|
||||
new MessageButton()
|
||||
.setEmoji('✅')
|
||||
.setCustomId(JSON.stringify({
|
||||
const row = new MessageActionRow().addComponents(
|
||||
new MessageButton()
|
||||
.setEmoji('✅')
|
||||
.setCustomId(
|
||||
JSON.stringify({
|
||||
m: 'MP_P',
|
||||
a: 'arp', // Add remaining playlist
|
||||
d: `${voiceChannel.id}$${linkData.videoId}$${linkData.list}`
|
||||
}))
|
||||
.setLabel(' Add the remaining songs in the playlist')
|
||||
.setStyle('PRIMARY')
|
||||
)
|
||||
})
|
||||
)
|
||||
.setLabel(' Add the remaining songs in the playlist')
|
||||
.setStyle('PRIMARY')
|
||||
);
|
||||
|
||||
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.ADDED_QUEUE(data.getRaw(), result)], components: [row] });
|
||||
}
|
||||
else
|
||||
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.ADDED_QUEUE(data.getRaw(), result)] });
|
||||
return await sendHybridInteractionMessageResponse(data, {
|
||||
embeds: [EMBEDS.ADDED_QUEUE(data, locale, result)],
|
||||
components: [row]
|
||||
});
|
||||
} else
|
||||
return await sendHybridInteractionMessageResponse(data, {
|
||||
embeds: [EMBEDS.ADDED_QUEUE(data, locale, result)]
|
||||
});
|
||||
} else {
|
||||
let result = await DiscordMusicPlayer.searchYouTubeByQuery(query);
|
||||
|
||||
@@ -324,7 +376,6 @@ export default class Play extends DiscordModule {
|
||||
instance.addTrackToQueue(result[0]);
|
||||
|
||||
if (result.length > 1) {
|
||||
|
||||
// TODO: Find a better logic than this
|
||||
// If the first result title is exact match with the query or first title contains half the space of the query, it's probably a sentence
|
||||
// then we don't need to show the search results
|
||||
@@ -333,37 +384,41 @@ export default class Play extends DiscordModule {
|
||||
// The query length is too long to fit in json
|
||||
if (query.length > 100 - 51) return;
|
||||
|
||||
const row = new MessageActionRow()
|
||||
.addComponents(
|
||||
new MessageButton()
|
||||
.setEmoji('🔎')
|
||||
.setCustomId(JSON.stringify({
|
||||
const row = new MessageActionRow().addComponents(
|
||||
new MessageButton()
|
||||
.setEmoji('🔎')
|
||||
.setCustomId(
|
||||
JSON.stringify({
|
||||
m: 'MP_S',
|
||||
a: 'search',
|
||||
d: {
|
||||
q: query
|
||||
}
|
||||
}))
|
||||
.setLabel(' Not this? Search!')
|
||||
.setStyle('PRIMARY')
|
||||
)
|
||||
})
|
||||
)
|
||||
.setLabel(' Not this? Search!')
|
||||
.setStyle('PRIMARY')
|
||||
);
|
||||
|
||||
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.ADDED_QUEUE(data.getRaw(), result[0])], components: [row] });
|
||||
return await sendHybridInteractionMessageResponse(data, {
|
||||
embeds: [EMBEDS.ADDED_QUEUE(data, locale, result[0])],
|
||||
components: [row]
|
||||
});
|
||||
}
|
||||
|
||||
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.ADDED_QUEUE(data.getRaw(), result[0])] });
|
||||
return await sendHybridInteractionMessageResponse(data, {
|
||||
embeds: [EMBEDS.ADDED_QUEUE(data, locale, result[0])]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isJSONValid(jsonString: string) {
|
||||
try {
|
||||
let o = JSON.parse(jsonString);
|
||||
if (o && typeof o === "object") {
|
||||
if (o && typeof o === 'object') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (e) { }
|
||||
} catch (e) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user