From 6d82b13e09ead99e9ca02b4a9828f6a356d948be Mon Sep 17 00:00:00 2001 From: Fijxu Date: Tue, 21 May 2024 23:47:49 -0400 Subject: [PATCH] Added mod a vips handling --- src/api/users.cr | 156 ----------------------------------- src/main.cr | 78 ++++++++++-------- src/routes/api/users.cr | 175 ++++++++++++++++++++++++++++++++++++++++ src/routes/routes.cr | 24 ++++++ src/twitchapi/gql.cr | 112 +++++++++---------------- 5 files changed, 280 insertions(+), 265 deletions(-) delete mode 100644 src/api/users.cr create mode 100644 src/routes/api/users.cr create mode 100644 src/routes/routes.cr diff --git a/src/api/users.cr b/src/api/users.cr deleted file mode 100644 index 80f9f4f..0000000 --- a/src/api/users.cr +++ /dev/null @@ -1,156 +0,0 @@ -require "json" -require "../twitchapi/*" - -# def fetchGQL(id : String) : JSON::Any -# GqlAPI.query(id) -# end - -# def fetchGQLchannel(id : String) : JSON::Any -# GqlAPI.channel(id) -# end - -# def fetchGQLbanned(id : String) : JSON::Any -# GqlAPI.isBanned(id) -# end - -module Users - def self.checkBanned(gql) - # puts "checkBanned: #{gql}" - # puts (typeof(gql)) - # if gql["userResultByLogin"] - # return false - # if gql["userResultByLogin"]["result"]? - # return true - # else - # return false - # end - # elsif gql["userResultByID"]? - # return false - # if gql["userResultByID"]["reason"]? - # return true - # else - # return false - # end - # else - # return true - # end - # if gql["userResultByLogin"]["reason"]? - # return true - # elsif gql["userResultByID"]["reason"]? - # return true - # else - # return false - # end - # return false - if GqlAPI.urb.includes? "login" - if gql["userResultByLogin"]["reason"]? - return gql["userResultByLogin"]["reason"] - else - return nil - end - elsif GqlAPI.urb.includes? "id" - if gql["userResultByID"]["reason"]? - return gql["userResultByID"]["reason"] - else - return nil - end - end - end - - def self.parseData(params) - # helixUsers = JSON.parse(HelixAPI.users(params)) - # begin - # login = helixUsers["data"][0]["login"] - # id = helixUsers["data"][0]["id"] - # rescue - # login = nil - # id = nil - # end - - # if login.nil? && params.has_key?("login") - # login = params["login"] - # elsif id.nil? && params.has_key?("id") - # id = params["id"] - # end - - # gqlUser = GqlAPI.user(id.to_s) - # gqlChannel = GqlAPI.channel(id.to_s) - - # a = Channel(JSON::Any).new - # spawn do - # a.send fetchGQL(id.to_s) - # end - login = params["login"]? - id = params["id"]? - - if login && (json = REDIS_DB.get(login)) - puts (JSON.parse(json)) - return json - elsif id && (json = REDIS_DB.get(id)) - return json - end - - # puts REDIS_DB.get(params["login"]?) - # puts REDIS_DB.get(params["id"]?) - # if (json = (REDIS_DB.get(params["login"]?.to_s))) || (json = (REDIS_DB.get(params["id"]?.to_s))) - # if ((json = REDIS_DB.get(id)) - # puts json - # return json - # end - - gql = GqlAPI.query(params) - - panels = gql["user"]["panels"] - banned = checkBanned(gql) - # puts(banned) - - json_data = [ - { - "cache" => { - "isCached" => false, - "expireTime" => nil : Int32, - }, - "banned" => false, - "reason" => banned, - "displayName" => gql["user"]["displayName"], - "login" => gql["user"]["login"], - "id" => gql["user"]["id"], - "bio" => gql["user"]["description"], - "follows" => nil, - "followers" => gql["user"]["followers"]["totalCount"], - "profileViewCount" => nil, # Always null - "panelCount" => panels.size, - "chatColor" => gql["user"]["chatColor"], - "logo" => gql["user"]["profileImageURL"], - "banner" => gql["user"]["bannerImageURL"], - "verifiedBot" => nil, # Deprecated by twitch - "createdAt" => gql["user"]["createdAt"], - "updatedAt" => gql["user"]["updatedAt"], - "deletedAt" => gql["user"]["deletedAt"], - "emotePrefix" => gql["user"]["emoticonPrefix"]["name"], - "roles" => { - "isAffiliate" => gql["user"]["roles"]["isAffiliate"], - "isPartner" => gql["user"]["roles"]["isPartner"], - "isStaff" => gql["user"]["roles"]["isStaff"], - }, - "badges" => gql["user"]["displayBadges"], - "chatterCount" => gql["user"]["channel"]["chatters"]["count"], - "chatSettings" => gql["user"]["chatSettings"], - "stream" => gql["user"]["stream"], - "lastBroadcast" => gql["user"]["lastBroadcast"], - "panels" => panels, - }, - ] - if banned != nil - json_data[0]["banned"] = true - end - - if id - REDIS_DB.set(id, json_data.to_json, ex: 5) - else - REDIS_DB.set(login.to_s, json_data.to_json, ex: 5) - end - - return json_data.to_json - end -end diff --git a/src/main.cr b/src/main.cr index 2dc0fe2..999cf72 100644 --- a/src/main.cr +++ b/src/main.cr @@ -2,11 +2,17 @@ require "http/server" require "kemal" require "json" require "uri" -require "./config" -require "./api/*" -require "./twitchapi/*" require "redis" +require "./config" +require "./routes/**" +require "./twitchapi/*" + +module TwAPI +end + +alias TA = TwAPI + CONFIG = Config.load Kemal.config.port = CONFIG.port REDIS_DB = Redis::PooledClient.new(unixsocket: CONFIG.redis_socket || nil, url: CONFIG.redis_url || nil) @@ -14,41 +20,43 @@ if REDIS_DB.ping puts "Connected to redis" end -before_all "/twitch/*" do |env| +before_all "/v2/*" do |env| env.response.content_type = "application/json" end -get "/twitch/user" do |env| - query = env.request.query - if query - params = URI::Params.parse(query) - if params.has_key?("login") - begin - Users.parseData(params) - rescue ex - env.response.status_code = 401 - err = {"error" => "#{ex.message}"} - err.to_json - end - elsif params.has_key?("id") - begin - Users.parseData(params) - rescue ex - env.response.status_code = 401 - err = {"error" => "#{ex.message}"} - err.to_json - end - else - env.response.status_code = 401 - err = {"error" => "Parameter 'login' or 'id' is missing"} - err.to_json - end - else - env.response.status_code = 401 - err = {"error" => "No query parameters found"} - err.to_json - end -end +TwAPI::Routing.register_all() + +# get "/v2/twitch/user" do |env| +# query = env.request.query +# if query +# params = URI::Params.parse(query) +# if params.has_key?("login") +# begin +# Users.parseData(params) +# rescue ex +# env.response.status_code = 401 +# err = {"error" => "#{ex.message}"} +# err.to_json +# end +# elsif params.has_key?("id") +# begin +# Users.parseData(params) +# rescue ex +# env.response.status_code = 401 +# err = {"error" => "#{ex.message}"} +# err.to_json +# end +# else +# env.response.status_code = 401 +# err = {"error" => "Parameter 'login' or 'id' is missing"} +# err.to_json +# end +# else +# env.response.status_code = 401 +# err = {"error" => "No query parameters found"} +# err.to_json +# end +# end {% if flag?(:release) || flag?(:production) %} Kemal.config.env = "production" if !ENV.has_key?("KEMAL_ENV") diff --git a/src/routes/api/users.cr b/src/routes/api/users.cr new file mode 100644 index 0000000..36bb029 --- /dev/null +++ b/src/routes/api/users.cr @@ -0,0 +1,175 @@ +module TwAPI::Routes::Twitch + extend self + + def checkBanned(gql) + if GqlAPI.urb.includes? "login" + if gql["userResultByLogin"]["reason"]? + return gql["userResultByLogin"]["reason"] + else + return nil + end + elsif GqlAPI.urb.includes? "id" + if gql["userResultByID"]["reason"]? + return gql["userResultByID"]["reason"] + else + return nil + end + end + end + + def parseData(params) + login = params["login"]? + id = params["id"]? + + if login && (json = REDIS_DB.get(login)) + puts(JSON.parse(json)) + return json + elsif id && (json = REDIS_DB.get(id)) + return json + end + + # puts REDIS_DB.get(params["login"]?) + # puts REDIS_DB.get(params["id"]?) + # if (json = (REDIS_DB.get(params["login"]?.to_s))) || (json = (REDIS_DB.get(params["id"]?.to_s))) + # if ((json = REDIS_DB.get(id)) + # puts json + # return json + # end + + gql = GqlAPI.queryUser(params) + puts gql + + panels = gql["user"]["panels"] + banned = checkBanned(gql) + # puts(banned) + + # Using JSON::Builder will be better I guess + json_data = [ + { + "cache" => { + "isCached" => false, + "expireTime" => nil : Int32, + }, + "banned" => false, + "reason" => banned, + "displayName" => gql["user"]["displayName"], + "login" => gql["user"]["login"], + "id" => gql["user"]["id"], + "bio" => gql["user"]["description"], + "follows" => nil, + "followers" => gql["user"]["followers"]["totalCount"], + "profileViewCount" => nil, # Always null + "panelCount" => panels.size, + "chatColor" => gql["user"]["chatColor"], + "logo" => gql["user"]["profileImageURL"], + "banner" => gql["user"]["bannerImageURL"], + "verifiedBot" => nil, # Deprecated by twitch + "createdAt" => gql["user"]["createdAt"], + "updatedAt" => gql["user"]["updatedAt"], + "deletedAt" => gql["user"]["deletedAt"], + "emotePrefix" => gql["user"]["emoticonPrefix"]["name"], + "roles" => { + "isAffiliate" => gql["user"]["roles"]["isAffiliate"], + "isPartner" => gql["user"]["roles"]["isPartner"], + "isStaff" => gql["user"]["roles"]["isStaff"], + }, + "badges" => gql["user"]["displayBadges"], + "chatterCount" => gql["user"]["channel"]["chatters"]["count"], + "chatSettings" => gql["user"]["chatSettings"], + "stream" => gql["user"]["stream"], + "lastBroadcast" => gql["user"]["lastBroadcast"], + "panels" => panels, + }, + ] + if banned != nil + json_data[0]["banned"] = true + end + + if id + REDIS_DB.set(id, json_data.to_json, ex: 5) + else + REDIS_DB.set(login.to_s, json_data.to_json, ex: 5) + end + + return json_data.to_json + end + + def user(env) + # TODO: Clean this mess shit of else if else if else if + if env.params.query + params = env.params.query + if params.has_key?("login") || params.has_key?("id") + begin + parseData(params) + rescue ex + env.response.status_code = 401 + err = {"error" => "#{ex.message}"} + err.to_json + end + elsif params.has_key?("id") + begin + parseData(params) + rescue ex + env.response.status_code = 401 + err = {"error" => "#{ex.message}"} + err.to_json + end + else + env.response.status_code = 401 + err = {"error" => "Parameter 'login' or 'id' is missing"} + err.to_json + end + else + env.response.status_code = 401 + err = {"error" => "No query parameters found"} + err.to_json + end + end + + def modvip(env) + if !env.params.query.has_key?("login") && !env.params.query.has_key?("id") + err = {"error" => "No query parameters found"} + return err.to_json + else + gql = GqlAPI.queryModsVips(env.params.query)["user"] + # puts (gql["user"]["mods"]["edges"].size.times) + i, j = 0, 0 + + json_data = JSON.build do |json| + json.object do + json.field "modCount", gql["mods"]["edges"].size.to_i + json.field "vipCount", gql["vips"]["edges"].size.to_i + json.field "mods" do + json.array do + while i < gql["mods"]["edges"].size # There is a better way to do this? + json.object do + json.field "id", gql["mods"]["edges"][i]["node"]["id"]? + json.field "login", gql["mods"]["edges"][i]["node"]["login"]? + json.field "displayName", gql["mods"]["edges"][i]["node"]["displayName"]? + json.field "grantedAt", gql["mods"]["edges"][i]["grantedAt"]? + i += 1 + end + end + end + json.field "vips" do + json.array do + while j < gql["vips"]["edges"].size + json.object do + # puts gql["vips"]["edges"][j]["node"]["id"] + json.field "id", gql["vips"]["edges"][j].try &.["node"]?.try &.["id"] + json.field "login", gql["vips"]["edges"][j].try &.["node"]["login"] || "" + json.field "displayName", gql["vips"]["edges"][j].try &.["node"]["displayName"] || "" + json.field "grantedAt", gql["vips"]["edges"][j]["grantedAt"]? + j += 1 + end + end + end + end + end + end + end + return json_data + end + # TODO: Add error message if query doesn't exist at all + end +end diff --git a/src/routes/routes.cr b/src/routes/routes.cr new file mode 100644 index 0000000..ea98bea --- /dev/null +++ b/src/routes/routes.cr @@ -0,0 +1,24 @@ +module TwAPI::Routing + extend self # To prevent writing self. at the start of every function + + # Thanks Invidious devs + {% for http_method in {"get", "post", "delete", "options", "patch", "put"} %} + + macro {{http_method.id}}(path, controller, method = :handle) + unless Kemal::Utils.path_starts_with_slash?(\{{path}}) + raise Kemal::Exceptions::InvalidPathStartException.new({{http_method}}, \{{path}}) + end + + Kemal::RouteHandler::INSTANCE.add_route({{http_method.upcase}}, \{{path}}) do |env| + \{{ controller }}.\{{ method.id }}(env) + end + end + + {% end %} + + def register_all + # get "/" + get "/v2/twitch/user", TwAPI::Routes::Twitch, :user + get "/v2/twitch/modvip", TwAPI::Routes::Twitch, :modvip + end +end diff --git a/src/twitchapi/gql.cr b/src/twitchapi/gql.cr index 2ef8acf..56f0bbe 100644 --- a/src/twitchapi/gql.cr +++ b/src/twitchapi/gql.cr @@ -1,7 +1,6 @@ -require "json" -require "uri" - module GqlAPI + extend self + @@headers = HTTP::Headers{ "Content-Type" => "application/json", # "Client-Id" => "kimne78kx3ncx6brgo4mv6wki5h1ko", # Can cause problems due to Client-Integrity @@ -10,11 +9,11 @@ module GqlAPI } @@urb : String = "fuck" - def self.urb + def urb return @@urb end - def self.userResultBy(params) + def userResultBy(params) if params.has_key?("id") return %(userResultByID(id: "#{params["id"]}")) else @@ -22,7 +21,7 @@ module GqlAPI end end - def self.loginOrID(params) + def loginOrID(params) if params.has_key?("id") return %(userResultByID(id: "#{params["id"]}")), %(user(id: "#{params["id"]}" lookupType: ALL)) else @@ -30,15 +29,7 @@ module GqlAPI end end - # def self.channel (params) - # if params.has_key?("id") - # return %(channel(id: "#{params["id"]}")) - # else - # return %(channel(name: "#{params["login"]}")) - # end - # end - - def self.user(params) + def user(params) if params.has_key?("id") return %(user(id: "#{params["id"]}" lookupType: ALL)) else @@ -46,27 +37,7 @@ module GqlAPI end end - def self.query(params) - # puts params - # id = params["id"] - - # data = { "query" => "{user(id:#{id}){ - # bannerImageURL, - # profileImageURL(width: 600), - # createdAt, - # updatedAt, - # deletedAt, - # chatColor, - # emoticonPrefix{name}, - # panels(hideExtensions: false){id,type}, - # follows(first: 1, after: buh, filter: ALL order: ASC){totalCount}, - # followers(first: 1, after: buh, order: ASC){totalCount}, - # roles{isStaff,isAffiliate,isPartner,isExtensionsDeveloper}, - # displayBadges(){setID, title, description,version}, - # chatSettings{blockLinks,chatDelayMs,slowModeDurationSeconds,followersOnlyDurationMinutes,isBroadcasterLanguageModeEnabled,isEmoteOnlyModeEnabled,isFastSubsModeEnabled,isSubscribersOnlyModeEnabled,isUniqueChatModeEnabled,requireVerifiedAccount,rules}, - # stream{averageFPS,bitrate,codec,createdAt,width,height,id,viewersCount,type,game{displayName}}, - # lastBroadcast{game{displayName},id,startedAt,title} - # }}" } + def queryUser(params) @@urb = userResultBy(params) query = %( query { @@ -157,36 +128,42 @@ module GqlAPI } } ) - # puts JSON.parse(gqlReqq)["data"] - # puts ("gqlReq JSON.parse typeof: #{typeof (gqlReqq)}") - # puts ("gqlReq JSON.parse typeof: #{typeof(JSON.parse(gqlReq(params, query))["data"])}") - # puts ("gqlReq JSON.parse.to-H typeof: #{typeof (gqlReqq.to_h)}") - # puts ((JSON.parse(gqlReq(params, query)))["data"])["userResultByID"]["reason"]? return (JSON.parse(gqlReq(params, query)))["data"] end - # def self.isBanned(id : String) - # data = {"query" => "{userResultByID(id:#{id}){... on UserDoesNotExist{reason}}}"} - # return JSON.parse(gqlReq(id, data.to_json))["data"]["userResultByID"] - # end + def queryModsVips(params) + query = %( + query { + #{user(params)} { + mods(first: 100) { + edges { + grantedAt + isActive + node { + displayName + id + login + } + } + } + vips(first: 100) { + edges { + grantedAt + node { + displayName + id + login + } + } + } + } + } + ) + return (JSON.parse(gqlReq(params, query)))["data"] + end - # def self.channel(id : String) - # data = {"query" => "{channel(id:#{id}){ - # chatters{count,moderators{login},vips{login}} - # }}"} - # return JSON.parse(gqlReq(id, data.to_json))["data"]["channel"] - # end - - # def self.query(id : String) - # data = { "query" => "{query(id:#{id}){ - # chatters{count,moderators{login},vips{login}} - # }}" } - # return JSON.parse(gqlReq(id, data.to_json))["data"]["channel"] - # end - - def self.gqlReq(params, query) + def gqlReq(params, query) data = {"query" => query} - # puts (typeof(data)) response = HTTP::Client.post(CONFIG.gqlEndpoint.to_s, headers: @@headers, body: data.to_json) if response.success? @@ -196,17 +173,4 @@ module GqlAPI raise "GQL Twitch API returned #{response.status_code}: #{response.body.to_s}" end end - - # def self.gqlReq(operation : String, login : String) - # gqlRequest = gqlOperation2() - # puts gqlRequest - - # response = HTTP::Client.post(CONFIG.gqlEndpoint.to_s, headers: @@headers, body: gqlRequest) - - # if response.success? - # return (response.body) - # else - # raise "GQL Twitch API returned #{response.status_code}: #{response.body.to_s}" - # end - # end end