640 lines
23 KiB
Crystal
640 lines
23 KiB
Crystal
module TwAPI::Datahandlers::Twitch
|
|
extend self
|
|
|
|
private macro error(message)
|
|
env.response.content_type = "application/json"
|
|
env.response.status_code = 403
|
|
error_message = {"error" => {{message}}, "twitchResponse" => gql}.to_json
|
|
return error_message
|
|
end
|
|
|
|
private macro twitchError
|
|
env.response.content_type = "application/json"
|
|
env.response.status_code = 403
|
|
error_message = {"error" => ex.message, "twitchResponse" => gql}.to_json
|
|
return error_message
|
|
end
|
|
|
|
private macro skipCache
|
|
env.params.query["skipCache"]?
|
|
end
|
|
|
|
def isBannedLogin(gql)
|
|
if gql["userResultByLogin"]["reason"]?
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
def isBannedId(gql)
|
|
if gql["userResultByID"]["reason"]?
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
def banReason(gql)
|
|
if gql["userResultByLogin"] == nil
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
def userLogin(env)
|
|
if skipCache != "true"
|
|
if (json = Utils::Redis.retrieveFromCache("user-login_#{env.params.query["login"].try &.split(',').to_s}"))
|
|
json = JSON.parse(json)
|
|
return json.to_json
|
|
end
|
|
end
|
|
|
|
begin
|
|
gql = GqlAPI.getUserByLogin(env.params.query)
|
|
rescue ex
|
|
twitchError
|
|
end
|
|
|
|
json_data = JSON.build do |json|
|
|
json.array do
|
|
gql.each do |gql|
|
|
gql = gql["data"]
|
|
|
|
if (gql["user"] == nil)
|
|
# v3 could add some feedback error message instead of an empty array.
|
|
nil # Do not add anything to the array. Just like api.ivr.fi does.
|
|
else
|
|
panels = gql["user"]["panels"]
|
|
banned = isBannedLogin(gql)
|
|
|
|
json.object do
|
|
json.field "banned", banned
|
|
if banned == true
|
|
json.field "banReason", gql["userResultByLogin"]["reason"]
|
|
end
|
|
json.field "displayName", gql["user"]["displayName"]
|
|
json.field "login", gql["user"]["login"]
|
|
json.field "id", gql["user"]["id"]
|
|
json.field "bio", gql["user"]["description"]
|
|
json.field "follows", nil
|
|
json.field "followers", gql["user"]["followers"]["totalCount"]
|
|
json.field "profileViewCount", nil # Always null
|
|
json.field "panelCount", panels.size
|
|
json.field "chatColor", gql["user"]["chatColor"]
|
|
json.field "logo", gql["user"]["profileImageURL"]
|
|
json.field "banner", gql["user"]["bannerImageURL"]
|
|
json.field "verifiedBot", nil # Deprecated by twitch
|
|
json.field "createdAt", gql["user"]["createdAt"]
|
|
json.field "updatedAt", gql["user"]["updatedAt"]
|
|
json.field "deletedAt", gql["user"]["deletedAt"]
|
|
json.field "emotePrefix", gql["user"]["emoticonPrefix"]["name"]
|
|
if gql["user"]["primaryTeam"] != nil
|
|
json.field "team", gql["user"]["primaryTeam"]["displayName"]
|
|
else
|
|
json.field "team", nil
|
|
end
|
|
if gql["user"]["primaryTeam"] != nil
|
|
json.field "contract", gql["user"]["programAgreement"]["type"]
|
|
else
|
|
json.field "contract", nil
|
|
end
|
|
json.field "roles", gql["user"]["roles"]
|
|
json.field "badges", gql["user"]["displayBadges"]
|
|
json.field "chatterCount", gql["user"]["channel"]["chatters"]["count"]
|
|
json.field "chatSettings", gql["user"]["chatSettings"]
|
|
json.field "stream", gql["user"]["stream"]
|
|
json.field "lastBroadcast", gql["user"]["lastBroadcast"]
|
|
json.field "panels", panels
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
begin
|
|
Utils::Redis.saveJson("user-login_#{env.params.query["login"].try &.split(',')}", json_data)
|
|
rescue ex
|
|
puts ex.message
|
|
end
|
|
|
|
return json_data
|
|
end
|
|
|
|
def userId(env)
|
|
if skipCache != "true"
|
|
if (json = Utils::Redis.retrieveFromCache("user-id_#{env.params.query["id"].try &.split(',').to_s}"))
|
|
json = JSON.parse(json)
|
|
return json.to_json
|
|
end
|
|
end
|
|
|
|
begin
|
|
gql = GqlAPI.getUserById(env.params.query)
|
|
rescue ex
|
|
twitchError
|
|
end
|
|
|
|
json_data = JSON.build do |json|
|
|
json.array do
|
|
gql.each do |gql|
|
|
pp gql = gql["data"]
|
|
pp typeof(gql)
|
|
|
|
if (gql["user"] == nil)
|
|
# v3 could add some feedback error message instead of an empty array.
|
|
# Do not add anything to the array. Just like api.ivr.fi does.
|
|
nil
|
|
else
|
|
panels = gql["user"]["panels"]
|
|
banned = isBannedId(gql)
|
|
|
|
json.object do
|
|
json.field "banned", banned
|
|
if banned == true
|
|
json.field "banReason", gql["userResultByID"]["reason"]
|
|
end
|
|
json.field "displayName", gql["user"]["displayName"]
|
|
json.field "login", gql["user"]["login"]
|
|
json.field "id", gql["user"]["id"]
|
|
json.field "bio", gql["user"]["description"]
|
|
json.field "follows", nil
|
|
json.field "followers", gql["user"]["followers"]["totalCount"]
|
|
json.field "profileViewCount", nil # Always null
|
|
json.field "panelCount", panels.size
|
|
json.field "chatColor", gql["user"]["chatColor"]
|
|
json.field "logo", gql["user"]["profileImageURL"]
|
|
json.field "banner", gql["user"]["bannerImageURL"]
|
|
json.field "verifiedBot", nil # Deprecated by twitch
|
|
json.field "createdAt", gql["user"]["createdAt"]
|
|
json.field "updatedAt", gql["user"]["updatedAt"]
|
|
json.field "deletedAt", gql["user"]["deletedAt"]
|
|
json.field "emotePrefix", gql["user"]["emoticonPrefix"]["name"]
|
|
|
|
# failed attempts
|
|
# pp "1: #{gql["user"]["primaryTeam"]? || nil}"
|
|
# pp "2: #{gql["user"].try &.["primaryTeam"]?.try &.["displayName"]? || nil}"
|
|
# json.field "team", gql["user"]["primaryTeam"].try &.["displayName"]?
|
|
#
|
|
if gql["user"]["primaryTeam"] != nil
|
|
json.field "team", gql["user"]["primaryTeam"]["displayName"]
|
|
else
|
|
json.field "team", nil
|
|
end
|
|
if gql["user"]["primaryTeam"] != nil
|
|
json.field "contract", gql["user"]["programAgreement"]["type"]
|
|
else
|
|
json.field "contract", nil
|
|
end
|
|
json.field "roles", gql["user"]["roles"]
|
|
json.field "badges", gql["user"]["displayBadges"]
|
|
json.field "chatterCount", gql["user"]["channel"]["chatters"]["count"]
|
|
json.field "chatSettings", gql["user"]["chatSettings"]
|
|
json.field "stream", gql["user"]["stream"]
|
|
json.field "lastBroadcast", gql["user"]["lastBroadcast"]
|
|
json.field "panels", panels
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
begin
|
|
Utils::Redis.saveJson("user-id_#{env.params.query["id"].try &.split(',')}", json_data)
|
|
rescue ex
|
|
puts ex.message
|
|
end
|
|
|
|
return json_data
|
|
end
|
|
|
|
def modvip(env)
|
|
channel = env.params.url["channel"]
|
|
|
|
if (json = Utils::Redis.retrieveFromCache("modvip-#{channel}")) && skipCache != "true"
|
|
json = JSON.parse(json)
|
|
json.as_h["cache"].as_h["isCached"] = JSON::Any.new(true)
|
|
json.as_h["cache"].as_h["expireTime"] = JSON::Any.new(Utils::Redis.getExpireTime("modvip-#{channel}"))
|
|
return json.to_json
|
|
end
|
|
|
|
begin
|
|
gql = GqlAPI.getModsVips(channel)
|
|
gql = gql["data"]["user"]
|
|
rescue ex
|
|
twitchError
|
|
end
|
|
|
|
if (gql == nil)
|
|
error("Specified channel does no exist")
|
|
end
|
|
|
|
json_data = JSON.build do |json|
|
|
json.object do
|
|
json.field "cache" do
|
|
json.object do
|
|
json.field "isCached", false
|
|
json.field "expireTime", nil
|
|
end
|
|
end
|
|
json.field "modCount", gql["mods"]["edges"].size
|
|
json.field "vipCount", gql["vips"]["edges"].size
|
|
json.field "mods" do
|
|
json.array do
|
|
gql["mods"]["edges"]?.try &.as_a.each do |edges|
|
|
json.object do
|
|
if edges.try &.["node"]? == nil
|
|
json.field "id", nil
|
|
json.field "login", nil
|
|
json.field "displayName", nil
|
|
json.field "grantedAt", nil
|
|
else
|
|
json.field "id", edges["node"]["id"]?
|
|
json.field "login", edges["node"]["login"]?
|
|
json.field "displayName", edges["node"]["displayName"]?
|
|
json.field "grantedAt", edges["grantedAt"]?
|
|
end
|
|
end
|
|
end
|
|
end
|
|
json.field "vips" do
|
|
json.array do
|
|
gql["vips"]["edges"]?.try &.as_a.each do |edges|
|
|
json.object do
|
|
if edges.try &.["node"]? == nil
|
|
json.field "id", nil
|
|
json.field "login", nil
|
|
json.field "displayName", nil
|
|
json.field "grantedAt", nil
|
|
else
|
|
json.field "id", edges["node"]["id"]
|
|
json.field "login", edges["node"]["login"]
|
|
json.field "displayName", edges["node"]["displayName"]
|
|
json.field "grantedAt", edges["grantedAt"]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
Utils::Redis.saveJson("modvip-#{channel}", json_data)
|
|
|
|
return json_data
|
|
end
|
|
|
|
# TODO: Add error message if query doesn't exist at all
|
|
|
|
def founders(env)
|
|
login = env.params.url["login"]
|
|
|
|
if (json = Utils::Redis.retrieveFromCache("founders-#{login}")) && skipCache != "true"
|
|
json = JSON.parse(json)
|
|
json.as_h["cache"].as_h["isCached"] = JSON::Any.new(true)
|
|
json.as_h["cache"].as_h["expireTime"] = JSON::Any.new(Utils::Redis.getExpireTime("founders-#{login}"))
|
|
return json.to_json
|
|
end
|
|
|
|
begin
|
|
gql = GqlAPI.getFounders(login)
|
|
gql = gql["data"]["user"]
|
|
rescue ex
|
|
twitchError
|
|
end
|
|
|
|
# TODO: use begin rescue for this too!
|
|
if (gql == nil)
|
|
error("Specified channel does no exist")
|
|
end
|
|
|
|
json_data = JSON.build do |json|
|
|
json.object do
|
|
json.field "cache" do
|
|
json.object do
|
|
json.field "isCached", false
|
|
json.field "expireTime", nil
|
|
end
|
|
end
|
|
json.field "foundersCount", gql["channel"]["founders"].size
|
|
json.field "founders" do
|
|
json.array do
|
|
gql["channel"]["founders"]?.try &.as_a.each do |founders|
|
|
json.object do
|
|
json.field "isSubscribed", founders["isSubscribed"]?
|
|
json.field "entitlementStart", founders["entitlementStart"]?
|
|
|
|
if founders.try &.["user"]? == nil
|
|
json.field "id", nil
|
|
json.field "login", nil
|
|
json.field "displayName", nil
|
|
else
|
|
json.field "id", founders["user"]["id"]
|
|
json.field "login", founders["user"]["login"]
|
|
json.field "displayName", founders["user"]["displayName"]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
Utils::Redis.saveJson("founders-#{login}", json_data)
|
|
|
|
return json_data
|
|
end
|
|
|
|
def clip(env)
|
|
slug = env.params.url["slug"]
|
|
|
|
if (json = Utils::Redis.retrieveFromCache("clip-#{slug}")) && skipCache != "true"
|
|
json = JSON.parse(json)
|
|
json.as_h["cache"].as_h["isCached"] = JSON::Any.new(true)
|
|
json.as_h["cache"].as_h["expireTime"] = JSON::Any.new(Utils::Redis.getExpireTime("clip-#{slug}"))
|
|
return json.to_json
|
|
end
|
|
|
|
begin
|
|
gql = GqlAPI.getClips(slug)
|
|
gql = gql["data"]["clip"]
|
|
rescue ex
|
|
twitchError
|
|
end
|
|
|
|
# TODO: use begin rescue for this too! (again)
|
|
if (gql == nil)
|
|
error("Slug does not exist")
|
|
end
|
|
|
|
json_data = JSON.build do |json|
|
|
json.object do
|
|
json.field "cache" do
|
|
json.object do
|
|
json.field "isCached", false
|
|
json.field "expireTime", nil
|
|
end
|
|
end
|
|
json.field "clip" do
|
|
json.object do
|
|
json.field "durationSeconds", gql["durationSeconds"]
|
|
json.field "id", gql["id"]
|
|
json.field "slug", gql["slug"]
|
|
json.field "url", gql["url"]
|
|
# TODO: Add tiny, small and medium previews
|
|
# "tiny": "https://clips-media-assets2.twitch.tv/MEtZsD4gcXcJb1kGQRbUYg/42368009784-offset-19328-preview-86x45.jpg",
|
|
# "small": "https://clips-media-assets2.twitch.tv/MEtZsD4gcXcJb1kGQRbUYg/42368009784-offset-19328-preview-260x147.jpg",
|
|
# "medium": "https://clips-media-assets2.twitch.tv/MEtZsD4gcXcJb1kGQRbUYg/42368009784-offset-19328-preview-480x272.jpg",
|
|
json.field "createdAt", gql["createdAt"]
|
|
json.field "title", gql["title"]
|
|
json.field "viewCount", gql["viewCount"]
|
|
# Game
|
|
json.field "game" do
|
|
json.object do
|
|
json.field "id", gql["game"]["id"]
|
|
json.field "name", gql["game"]["name"]
|
|
end
|
|
end
|
|
# Broadcaster
|
|
json.field "broadcaster" do
|
|
json.object do
|
|
json.field "id", gql["broadcaster"]["id"]
|
|
json.field "displayName", gql["broadcaster"]["displayName"]
|
|
end
|
|
end
|
|
|
|
# Curator
|
|
json.field "curator" do
|
|
json.object do
|
|
json.field "id", gql["curator"]["id"]
|
|
json.field "displayName", gql["curator"]["displayName"]
|
|
end
|
|
end
|
|
# Array of available video qualities
|
|
json.field "videoQualities" do
|
|
json.array do
|
|
gql["videoQualities"]?.try &.as_a.each do |videoQualities|
|
|
json.object do
|
|
if videoQualities.try &.["frameRate"]? == nil
|
|
json.field "error", "No data"
|
|
else
|
|
json.field "frameRate", videoQualities["frameRate"]
|
|
json.field "quality", videoQualities["quality"]
|
|
json.field "sourceURL", videoQualities["sourceURL"]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
# TODO: get ClipKey
|
|
# "clipKey": "?sig=5cc70f40c61313db289a132ab58c7b27d16220ab&token=%7B%22authorization%22:%7B%22forbidden%22:false,%22reason%22:%22%22%7D,%22clip_uri%22:%22https://production.assets.clips.twitchcdn.net/WclEPrSCziCGI7bKo8Md-w/AT-cm%257CWclEPrSCziCGI7bKo8Md-w.mp4%22,%22clip_slug%22:%22AltruisticSarcasticQuailSoBayed-tILOIIYjtsqjwx37%22,%22device_id%22:null,%22expires%22:1716487243,%22user_id%22:%22%22,%22version%22:2%7D"
|
|
json.field "clipKey", "How the fuck I get this shit"
|
|
end
|
|
end
|
|
end
|
|
end
|
|
Utils::Redis.saveJson("clip-#{slug}", json_data)
|
|
|
|
return json_data
|
|
end
|
|
|
|
def subage(env)
|
|
user = env.params.url["user"]
|
|
channel = env.params.url["channel"]
|
|
|
|
if (json = Utils::Redis.retrieveFromCache("subage-user_#{user}_channel_#{channel}")) && skipCache != "true"
|
|
json = JSON.parse(json)
|
|
json.as_h["cache"].as_h["isCached"] = JSON::Any.new(true)
|
|
json.as_h["cache"].as_h["expireTime"] = JSON::Any.new(Utils::Redis.getExpireTime("subage-user_#{user}_channel_#{channel}"))
|
|
return json.to_json
|
|
end
|
|
|
|
begin
|
|
gql = GqlAPI.getSubage(user, channel)
|
|
gql = gql["data"]
|
|
rescue ex
|
|
if (gql == nil)
|
|
error("Channel does not exist")
|
|
end
|
|
twitchError
|
|
end
|
|
|
|
json_data = JSON.build do |json|
|
|
json.object do
|
|
json.field "cache" do
|
|
json.object do
|
|
json.field "isCached", false
|
|
json.field "expireTime", nil
|
|
end
|
|
end
|
|
json.field "user" do
|
|
json.object do
|
|
json.field "id", gql["userData"]["id"]?
|
|
json.field "login", gql["userData"]["login"]?
|
|
json.field "displayName", gql["userData"]["displayName"]?
|
|
end
|
|
end
|
|
json.field "channel" do
|
|
json.object do
|
|
json.field "id", gql["channelData"]["id"]?
|
|
json.field "login", gql["channelData"]["login"]?
|
|
json.field "displayName", gql["channelData"]["displayName"]?
|
|
end
|
|
end
|
|
json.field "statusHidden", false # TODO: what is this? I need to get it NOW!
|
|
json.field "followedAt", gql["info"]["relationship"]["followedAt"]
|
|
if (gql["info"]["relationship"]["streak"] == nil) || (gql["info"]["relationship"]["streak"]["elapsedDays"]? == 0)
|
|
json.field "streak", nil
|
|
else
|
|
json.field "streak" do
|
|
json.object do
|
|
json.field "elapsedDays", gql["info"]["relationship"]["streak"]["elapsedDays"]?
|
|
json.field "daysRemaining", gql["info"]["relationship"]["streak"]["daysRemaining"]?
|
|
json.field "months", gql["info"]["relationship"]["streak"]["months"]?
|
|
json.field "end", gql["info"]["relationship"]["streak"]["end"]?
|
|
json.field "start", gql["info"]["relationship"]["streak"]["elapsedDays"]?
|
|
end
|
|
end
|
|
end
|
|
if (gql["info"]["relationship"]["cumulative"] == nil) || (gql["info"]["relationship"]["cumulative"]["elapsedDays"]? == 0)
|
|
json.field "cumulative", nil
|
|
else
|
|
json.field "cumulative" do
|
|
json.object do
|
|
json.field "elapsedDays", gql["info"]["relationship"]["cumulative"]["elapsedDays"]?
|
|
json.field "daysRemaining", gql["info"]["relationship"]["cumulative"]["daysRemaining"]?
|
|
json.field "months", gql["info"]["relationship"]["cumulative"]["months"]?
|
|
json.field "end", gql["info"]["relationship"]["cumulative"]["end"]?
|
|
json.field "start", gql["info"]["relationship"]["cumulative"]["elapsedDays"]?
|
|
end
|
|
end
|
|
end
|
|
if (gql["info"]["relationship"]["meta"] == nil)
|
|
json.field "meta", nil
|
|
else
|
|
json.field "meta" do
|
|
json.object do
|
|
json.field "type", nil # TODO: get the type of the sub, could be paid or something else gql["info"]["relationship"]["meta"][""]
|
|
json.field "tier", (gql["info"]["relationship"]["meta"]["tier"].to_s.to_i / 1000)
|
|
json.field "endsAt", gql["info"]["relationship"]["meta"]["endsAt"]
|
|
json.field "renewsAt", gql["info"]["relationship"]["meta"]["renewsAt"]
|
|
json.field "giftMeta", nil # TODO: fill giftMeta is sub is gifted
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
Utils::Redis.saveJson("subage-user_#{user}_channel_#{channel}", json_data)
|
|
|
|
return json_data
|
|
end
|
|
|
|
def emotes(env)
|
|
emoteName = env.params.url["emote"]
|
|
|
|
if (json = Utils::Redis.retrieveFromCache("emotes-#{emoteName}")) && skipCache != "true"
|
|
json = JSON.parse(json)
|
|
json.as_h["cache"].as_h["isCached"] = JSON::Any.new(true)
|
|
json.as_h["cache"].as_h["expireTime"] = JSON::Any.new(Utils::Redis.getExpireTime("emotes-#{emoteName}"))
|
|
return json.to_json
|
|
end
|
|
|
|
begin
|
|
gql = GqlAPI.getEmotes(emoteName)
|
|
gql = gql["data"]["emote"]
|
|
if gql == nil
|
|
raise "Emote does not exist"
|
|
end
|
|
rescue ex
|
|
twitchError
|
|
end
|
|
|
|
json_data = JSON.build do |json|
|
|
json.object do
|
|
json.field "cache" do
|
|
json.object do
|
|
json.field "isCached", false
|
|
json.field "expireTime", nil
|
|
end
|
|
end
|
|
json.field "channelName", gql["owner"]["displayName"]
|
|
json.field "channelLogin", gql["owner"]["login"]
|
|
json.field "channelID", gql["owner"]["id"]
|
|
json.field "artist", nil # TODO: ADD SUPPORT FOR ARTIST OBJECT
|
|
json.field "emoteID", gql["id"]
|
|
json.field "emoteCode", gql["text"]
|
|
json.field "emoteSetID", gql["setID"]
|
|
json.field "emoteAssetType", gql["assetType"]
|
|
json.field "emoteState", gql["state"]
|
|
json.field "emoteType", gql["type"]
|
|
json.field "emoteTier", gql["subscriptionTier"]
|
|
end
|
|
end
|
|
Utils::Redis.saveJson("emotes-#{emoteName}", json_data)
|
|
|
|
return json_data
|
|
end
|
|
|
|
def channelEmotes(env)
|
|
gql = GqlAPI.getChannelEmotes(env.params.query["channel"]) # TODO: Add support for IDs an not just the channel name
|
|
|
|
json_data = JSON.build do |json|
|
|
json.object do
|
|
json.field "cache" do
|
|
json.object do
|
|
json.field "isCached", false
|
|
json.field "expireTime", nil
|
|
end
|
|
end
|
|
json.field "subProducts" do
|
|
json.array do
|
|
gql["user"]["subEmotes"]?.try &.as_a.each do |subEmotes|
|
|
json.object do
|
|
json.field "displayName", subEmotes["displayName"]
|
|
json.field "emoteSetID", subEmotes["id"]
|
|
json.field "tier", subEmotes["tier"]
|
|
json.field "emotes" do
|
|
json.array do
|
|
subEmotes["emotes"]?.try &.as_a.each do |emotes|
|
|
json.object do
|
|
json.field "id", emotes["id"]
|
|
json.field "setID", emotes["setID"]
|
|
json.field "code", emotes["text"]
|
|
json.field "type", emotes["type"]
|
|
json.field "assetType", emotes["assetType"]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
# TODO: Emotes available by donating bits
|
|
# # # # # #
|
|
# json.field "bitEmotes" do
|
|
# json.array do
|
|
# end
|
|
json.field "localEmotes" do
|
|
json.array do
|
|
json.object do
|
|
json.field "id", gql["user"]["channel"]["localEmoteSets"][0]["id"] # TODO: Detect if channel has more than one localEmoteSets (since it's a fucking array)
|
|
json.field "emotes" do
|
|
json.array do
|
|
gql["user"]["channel"]["localEmoteSets"][0]["localEmotes"]?.try &.as_a.each do |localEmotes|
|
|
json.object do
|
|
json.field "artist", localEmotes["artist"] # TODO: Add support for artist (again)
|
|
json.field "id", localEmotes["id"]
|
|
json.field "type", localEmotes["type"]
|
|
json.field "assetType", localEmotes["assetType"]
|
|
json.field "code", localEmotes["text"]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|