diff --git a/src/config.cr b/src/config.cr index ffb6276..0932d77 100644 --- a/src/config.cr +++ b/src/config.cr @@ -5,8 +5,8 @@ class Config property port : Int32 = 8090 property db : String = "./db.sqlite3" - property dbTableName : String = "files" property redisUrl : String = "127.0.0.1:6379" + property log_level : LogLevel = LogLevel::Debug def self.load config_file = "./config.yml" diff --git a/src/handling.cr b/src/handling.cr index 54ff988..5a0e6d5 100644 --- a/src/handling.cr +++ b/src/handling.cr @@ -47,38 +47,38 @@ module Handlers end end end - # badge_id = [] of String - # badge_de = [] of String - # badge_im = [] of String - # badges_tuple[:bttv]?.try &.as_a.each do |item| - # if !badge_id.includes?(item["badge"]["type"]) - # badge_id << item["badge"]["type"].to_s - # badge_de << item["badge"]["description"].to_s - # badge_im << item["badge"]["svg"].to_s - # end - # end - # pp badge_id - # pp badge_de - # pp badge_im - # j.field "bttv" do - # j.object do - # j.field "badges" do - # j.array do - # badge_de.each do |badgename| - # j.object do - # j.field "name", badgename - # j.field "url", badgename - # end - # end - # end - # end - # end - # end + badge_id = [] of Int32 + badge_name = [] of String + badge_url = [] of String + badges_tuple[:bttv]?.try &.as_a.each do |item| + if !badge_id.includes?(item["badge"]["type"]) + badge_id << item["badge"]["type"].to_s.to_i + badge_name << item["badge"]["description"].to_s + badge_url << item["badge"]["svg"].to_s + end + end + badges = badge_id.zip(badge_name, badge_url) + j.field "bttv" do + j.object do + j.field "badges" do + j.array do + badges.each do |id, name, url| + j.object do + j.field "name", name + j.field "id", id + j.field "url", url + j.field "users", "xd" + end + end + end + end + end + end j.field "ffz" do j.object do j.field "badges" do j.array do - badges_tuple[:ffz]["badges"]?.try &.as_a.each do |item| + badges_tuple[:ffz]?.try &.["badges"]?.try &.as_a.each do |item| j.object do j.field "id", item["id"] j.field "name", item["name"] @@ -136,11 +136,31 @@ module Handlers end end - def parse_7tv_paints(env) - paints = Providers::SevenTV.get_all_paints + # 7TV + def parse_7tv_badges(env) + a = Providers::SevenTV.get_all_badges JSON.build do |j| j.object do - j.field "paints", paints["data"]["cosmetics"]["paints"] + j.field "badges", a["data"]["cosmetics"]["badges"] + end + end + end + + def parse_7tv_paints(env) + a = Providers::SevenTV.get_all_paints + JSON.build do |j| + j.object do + j.field "paints", a["data"]["cosmetics"]["paints"] + end + end + end + + def parse_7tv_user_cosmetics(env) + user_id = env.params.url["user"] + a = Providers::SevenTV.get_user_cosmetics(user_id) + JSON.build do |j| + j.object do + j.field "badges", a["data"]["user"]["cosmetics"] end end end diff --git a/src/logger.cr b/src/logger.cr new file mode 100644 index 0000000..dc2e5fa --- /dev/null +++ b/src/logger.cr @@ -0,0 +1,70 @@ +# https://github.com/iv-org/invidious/blob/master/src/invidious/helpers/logger.cr +enum LogLevel + All = 0 + Trace = 1 + Debug = 2 + Info = 3 + Warn = 4 + Error = 5 + Fatal = 6 + Off = 7 +end + +class LogHandler < Kemal::BaseLogHandler + def initialize(@io : IO = STDOUT, @level = LogLevel::Debug) + end + + def call(context : HTTP::Server::Context) + elapsed_time = Time.measure { call_next(context) } + elapsed_text = elapsed_text(elapsed_time) + + # Default: full path with parameters + requested_url = context.request.resource + + # Try not to log search queries passed as GET parameters during normal use + # (They will still be logged if log level is 'Debug' or 'Trace') + if @level > LogLevel::Debug && ( + requested_url.downcase.includes?("search") || requested_url.downcase.includes?("q=") + ) + # Log only the path + requested_url = context.request.path + end + + info("#{context.response.status_code} #{context.request.method} #{requested_url} #{elapsed_text}") + + context + end + + def puts(message : String) + @io << message << '\n' + @io.flush + end + + def write(message : String) + @io << message + @io.flush + end + + def set_log_level(level : String) + @level = LogLevel.parse(level) + end + + def set_log_level(level : LogLevel) + @level = level + end + + {% for level in %w(trace debug info warn error fatal) %} + def {{level.id}}(message : String) + if LogLevel::{{level.id.capitalize}} >= @level + puts("#{Time.utc} [{{level.id}}] #{message}") + end + end + {% end %} + + private def elapsed_text(elapsed) + millis = elapsed.total_milliseconds + return "#{millis.round(2)}ms" if millis >= 1 + + "#{(millis * 1000).round(2)}µs" + end +end diff --git a/src/providers/7tv.cr b/src/providers/7tv.cr index 08a12ec..a26608a 100644 --- a/src/providers/7tv.cr +++ b/src/providers/7tv.cr @@ -7,7 +7,13 @@ module Providers::SevenTV end def get_user_cosmetics(user_id : String) - seventv_user_id = get_seventv_user_id(user_id)["user"]["id"] + stored_used_id = SQL.query_one? "SELECT twitch_id FROM seventv_ids WHERE twitch_id = ? LIMIT 1;", user_id, as: String + if user_id != stored_used_id + seventv_user_id = get_seventv_user_id(user_id)["user"]["id"].to_s + SQL.exec "INSERT OR IGNORE INTO seventv_ids (twitch_id, seventv_id) VALUES (?, ?)", user_id, seventv_user_id + else + stv_id = SQL.query_one "SELECT seventv_id FROM seventv_ids WHERE twitch_id = ?", user_id, as: String + end query = %q( query GetUserCosmetics($id: ObjectID!) { user(id: $id) { @@ -26,7 +32,7 @@ query GetUserCosmetics($id: ObjectID!) { j.field "operationName", "GetUserCosmetics" j.field "variables" do j.object do - j.field "id", seventv_user_id + j.field "id", stv_id end end j.field "query", query @@ -37,7 +43,7 @@ query GetUserCosmetics($id: ObjectID!) { body: data) if response.success? - response.body + JSON.parse(response.body) else raise "7tv is retarded: #{response.body}" end @@ -62,6 +68,13 @@ query GetCosmestics($list: [ObjectID!]) { color __typename } + shadows { + x_offset + y_offset + radius + color + __typename + } __typename } } @@ -69,10 +82,38 @@ query GetCosmestics($list: [ObjectID!]) { data = JSON.build do |j| j.object do j.field "operationName", "GetCosmestics" - # j.field "variables" do - # j.object do - # end - # end + j.field "query", query + end + end + response = HTTP::Client.post("https://7tv.io/v3/gql", + headers: HTTP::Headers{"Content-Type" => "application/json"}, + body: data) + + if response.success? + JSON.parse(response.body) + else + raise "7tv is retarded: #{response.body}" + end + end + + def get_all_badges + query = %q( +query GetCosmestics($list: [ObjectID!]) { + cosmetics(list: $list) { + badges { + id + kind + name + tooltip + tag + __typename + } + __typename + } +}) + data = JSON.build do |j| + j.object do + j.field "operationName", "GetCosmestics" j.field "query", query end end diff --git a/src/providers/main.cr b/src/providers/main.cr index 82f0ea3..03e8a7d 100644 --- a/src/providers/main.cr +++ b/src/providers/main.cr @@ -11,7 +11,7 @@ module Providers # dankchat = Channel(JSON::Any).new # ffz = Channel(JSON::Any).new info = [Channel(JSON::Any).new, Channel(JSON::Any).new, Channel(JSON::Any).new, Channel(JSON::Any).new, Channel(JSON::Any).new, Channel(JSON::Any).new] - # Compile with -Dpreview_mt + # Compile with -Dpreview_mt for multithreading spawn { info[0].send(Providers::Chatterino.get_badges) } spawn { info[1].send(Providers::BTTV.get_badges) } spawn { info[2].send(Providers::FFZ.get_badges) } @@ -25,7 +25,7 @@ module Providers ffz: info[2].receive, dankchat: info[3].receive, chatty: info[4].receive, - # seventv: info[5].receive, + # seventv: info[5].receive, } return items end diff --git a/src/utils.cr b/src/utils.cr new file mode 100644 index 0000000..0f9d2f4 --- /dev/null +++ b/src/utils.cr @@ -0,0 +1,17 @@ +module Utils + extend self + + def create_db + if !SQL.query_one "SELECT EXISTS (SELECT 1 FROM sqlite_schema WHERE type='table' AND name='seventv_ids') + AND EXISTS (SELECT 1 FROM sqlite_schema WHERE type='table' AND name='seventv_ids');", as: Bool + LOGGER.info "Creating sqlite3 database at '#{CONFIG.db}'" + begin + SQL.exec "CREATE TABLE IF NOT EXISTS seventv_ids (twitch_id text UNIQUE, seventv_id text UNIQUE, username text UNIQUE)" + # SQL.exec "CREATE TABLE IF NOT EXISTS #{CONFIG.ipTableName} (ip text UNIQUE, count integer DEFAULT 0, date integer)" + rescue ex + LOGGER.fatal "#{ex.message}" + exit(1) + end + end + end +end diff --git a/src/vanity-tester-backend.cr b/src/vanity-tester-backend.cr index 64f894d..ac97e60 100644 --- a/src/vanity-tester-backend.cr +++ b/src/vanity-tester-backend.cr @@ -6,6 +6,8 @@ require "http/client" require "./config" require "./handling" +require "./logger" +require "./utils" require "./providers/**" CONFIG = Config.load @@ -14,6 +16,9 @@ Kemal.config.shutdown_message = false Kemal.config.app_name = "vanity-tester-backend" SQL = DB.open("sqlite3://#{CONFIG.db}") REDIS_DB = Redis::Client.new(CONFIG.redisUrl) +LOGGER = LogHandler.new(STDOUT, CONFIG.log_level) + +Utils.create_db HEADERS = HTTP::Headers{"User-Agent" => "Vanity-Tester-Backend/0.1.0"} @@ -21,16 +26,16 @@ get "/badges" do |env| Handlers.parse_badges(env) end -get "/7tv" do |env| - Handlers.parse_badges(env) +get "/7tv/badges" do |env| + Handlers.parse_7tv_badges(env) end get "/7tv/paints" do |env| Handlers.parse_7tv_paints(env) end -# get "/7tv/badges" do |env| -# Handlers.parse_7tv_badges(env) -# end +get "/7tv/cosmetics/:user" do |env| + Handlers.parse_7tv_user_cosmetics(env) +end Kemal.run