0.2.0
This commit is contained in:
parent
7fbc39436b
commit
c085b71ede
17 changed files with 226065 additions and 379 deletions
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Binaries
|
||||||
|
bin
|
||||||
|
|
||||||
|
# User config
|
||||||
|
config/config.yml
|
||||||
|
|
||||||
|
# libs
|
||||||
|
lib
|
||||||
|
|
||||||
|
# Syncthing
|
||||||
|
.stfolder
|
24
README.md
24
README.md
|
@ -1,16 +1,24 @@
|
||||||
|
## How to run
|
||||||
|
|
||||||
|
- Clone repo
|
||||||
|
- Copy the `config/config.example.yml` to `config/config.yml`
|
||||||
|
- Fill every option
|
||||||
|
- Compile using `shards build --release`
|
||||||
|
- Run `./bin/ivr-api-crystal`
|
||||||
|
|
||||||
### TODO
|
### TODO
|
||||||
|
|
||||||
- ~Use Redis to cache replies and add a new key to report for how much time the JSON is going to be stored in cache (**Already started, but I need to fix it!!**)~ (Only done for the user endpoint)
|
- ~~Use Redis to cache replies and add a new key to report for how much time the JSON is going to be stored in cache~~ Done.
|
||||||
- ~Add other endpoints from api.ivr.fi~ (Almost everything)
|
- ~~Add other endpoints from api.ivr.fi~~ Almost done.
|
||||||
- CAPTURE MORE THAN 1 PARAM `?login=fuck,fuck`
|
- ~~Capture more than 1 param: `?login=fijxu,fijxu`~~ Done.
|
||||||
|
|
||||||
### NOT SO IMPORTANT TODO
|
### NOT SO IMPORTANT TODO
|
||||||
|
|
||||||
- Rate limiting (can be done in the reverse proxy side)
|
- ~~STOP USING KEMAL FOR SIMPLE THINGS~~ Nevermind, kemal is pretty useful and it should stay
|
||||||
- ~Better shitcode~ Better goodcode
|
- `programAgreement { type }` in the Next commit.
|
||||||
- ~STOP USING KEMAL FOR SIMPLE THINGS!~ Nevermind, kemal is pretty useful and it should stay
|
- Guide on how to get the tokens to get it working
|
||||||
|
- Kick API
|
||||||
|
|
||||||
### Suggestions
|
### Suggestions
|
||||||
|
|
||||||
3:45 RyanPotat: add team
|
~~3:45 RyanPotat: add team~~ Next commit.
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# Please fill everything
|
# Please fill everything
|
||||||
|
|
||||||
helixOAuth: ""
|
helixOAuth: ""
|
||||||
helixClientID: ""
|
helixClientID: ""
|
||||||
gqlOAuth: ""
|
gqlOAuth: ""
|
||||||
gqlClientID: ""
|
gqlClientID: ""
|
||||||
apiEndpoint: "https://api.twitch.tv/helix"
|
apiEndpoint: "https://api.twitch.tv/helix"
|
||||||
gqlEndpoint: "https://gql.twitch.tv/gql"
|
gqlEndpoint: "https://gql.twitch.tv/gql"
|
||||||
redis_url: "tcp://127.0.0.1:6379"
|
redis_addr: "127.0.0.1:6379"
|
||||||
port: 8080
|
redis_database: 1
|
||||||
|
port: 8081
|
||||||
|
|
47398
schema.graphql
Normal file
47398
schema.graphql
Normal file
File diff suppressed because it is too large
Load diff
178032
schema.json
Normal file
178032
schema.json
Normal file
File diff suppressed because it is too large
Load diff
12
shard.lock
12
shard.lock
|
@ -4,6 +4,10 @@ shards:
|
||||||
git: https://github.com/sija/backtracer.cr.git
|
git: https://github.com/sija/backtracer.cr.git
|
||||||
version: 1.2.2
|
version: 1.2.2
|
||||||
|
|
||||||
|
db:
|
||||||
|
git: https://github.com/crystal-lang/crystal-db.git
|
||||||
|
version: 0.13.1
|
||||||
|
|
||||||
exception_page:
|
exception_page:
|
||||||
git: https://github.com/crystal-loot/exception_page.git
|
git: https://github.com/crystal-loot/exception_page.git
|
||||||
version: 0.4.1
|
version: 0.4.1
|
||||||
|
@ -12,15 +16,11 @@ shards:
|
||||||
git: https://github.com/kemalcr/kemal.git
|
git: https://github.com/kemalcr/kemal.git
|
||||||
version: 1.5.0
|
version: 1.5.0
|
||||||
|
|
||||||
pool:
|
|
||||||
git: https://github.com/ysbaddaden/pool.git
|
|
||||||
version: 0.2.4
|
|
||||||
|
|
||||||
radix:
|
radix:
|
||||||
git: https://github.com/luislavena/radix.git
|
git: https://github.com/luislavena/radix.git
|
||||||
version: 0.4.1
|
version: 0.4.1
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
git: https://github.com/stefanwille/crystal-redis.git
|
git: https://github.com/jgaskins/redis.git
|
||||||
version: 2.9.1
|
version: 0.9.0
|
||||||
|
|
||||||
|
|
30
shard.yml
30
shard.yml
|
@ -1,30 +1,22 @@
|
||||||
name: ivr-api
|
name: ivr-crystal
|
||||||
version: 0.1.0
|
version: 0.2.0
|
||||||
|
|
||||||
targets:
|
targets:
|
||||||
invidious:
|
ivr-api-crystal:
|
||||||
main: src/main.cr
|
main: src/main.cr
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
kemal:
|
kemal:
|
||||||
github: kemalcr/kemal
|
github: kemalcr/kemal
|
||||||
|
# redis:
|
||||||
|
# github: stefanwille/crystal-redis
|
||||||
redis:
|
redis:
|
||||||
github: stefanwille/crystal-redis
|
github: jgaskins/redis
|
||||||
|
|
||||||
|
authors:
|
||||||
|
- Fijxu
|
||||||
|
|
||||||
# authors:
|
description: |
|
||||||
# - name <email@example.com>
|
Drop-in api.ivr.fi replacement made using Crystal.
|
||||||
|
|
||||||
# description: |
|
license: Unlicense
|
||||||
# Short description of ivr-api
|
|
||||||
|
|
||||||
# dependencies:
|
|
||||||
# pg:
|
|
||||||
# github: will/crystal-pg
|
|
||||||
# version: "~> 0.5"
|
|
||||||
|
|
||||||
# development_dependencies:
|
|
||||||
# webmock:
|
|
||||||
# github: manastech/webmock.cr
|
|
||||||
|
|
||||||
# license: MIT
|
|
||||||
|
|
|
@ -3,31 +3,21 @@ require "yaml"
|
||||||
class Config
|
class Config
|
||||||
include YAML::Serializable
|
include YAML::Serializable
|
||||||
|
|
||||||
property helixOAuth : String?
|
property helixOAuth : String
|
||||||
property helixClientID : String?
|
property helixClientID : String
|
||||||
property gqlOAuth : String?
|
property gqlOAuth : String
|
||||||
property gqlClientID : String?
|
property gqlClientID : String
|
||||||
property apiEndpoint : String?
|
property apiEndpoint : String? = "https://api.twitch.tv/helix"
|
||||||
property gqlEndpoint : String?
|
property gqlEndpoint : String? = "https://gql.twitch.tv/gql"
|
||||||
property redis_url : String?
|
property redis_addr : String? = "127.0.0.1:6379"
|
||||||
property redis_socket : String?
|
property redis_socket : String?
|
||||||
|
property redis_database : Int32?
|
||||||
property port : Int32 = 8080
|
property port : Int32 = 8080
|
||||||
|
|
||||||
def self.load
|
def self.load
|
||||||
config_file = "config/config.yml"
|
config_file = "config/config.yml"
|
||||||
config_yaml = File.read(config_file)
|
config_yaml = File.read(config_file)
|
||||||
config = Config.from_yaml(config_yaml)
|
config = Config.from_yaml(config_yaml)
|
||||||
|
|
||||||
if config.helixOAuth.to_s.empty?
|
|
||||||
puts "Config: 'helixOAuth' is required/can't be empty"
|
|
||||||
exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
if config.helixClientID.to_s.empty?
|
|
||||||
puts "Config: 'helixOAuth' is required/can't be empty"
|
|
||||||
exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
return config
|
return config
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,159 +1,215 @@
|
||||||
module TwAPI::Routes::Twitch
|
module TwAPI::Datahandlers::Twitch
|
||||||
extend self
|
extend self
|
||||||
|
|
||||||
def checkBanned(gql)
|
private macro error(message)
|
||||||
if GqlAPI.urb.includes? "login"
|
env.response.content_type = "application/json"
|
||||||
if gql["userResultByLogin"]["reason"]?
|
env.response.status_code = 403
|
||||||
return gql["userResultByLogin"]["reason"]
|
error_message = {"error" => {{message}}, "twitchResponse" => gql}.to_json
|
||||||
else
|
return error_message
|
||||||
return nil
|
end
|
||||||
end
|
|
||||||
elsif GqlAPI.urb.includes? "id"
|
private macro twitchError
|
||||||
if gql["userResultByID"]["reason"]?
|
env.response.content_type = "application/json"
|
||||||
return gql["userResultByID"]["reason"]
|
env.response.status_code = 403
|
||||||
else
|
error_message = {"error" => ex.message, "twitchResponse" => gql}.to_json
|
||||||
return nil
|
return error_message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private macro skipCache
|
||||||
|
env.params.query["skipCache"]?
|
||||||
|
end
|
||||||
|
|
||||||
|
def isBannedLogin(gql)
|
||||||
|
if gql["userResultByLogin"]["reason"]?
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def parseData(params)
|
def isBannedId(gql)
|
||||||
if !params.has_key?("login") && !params.has_key?("id")
|
if gql["userResultByID"]["reason"]?
|
||||||
err = {"error" => "No query parameters found"}
|
return true
|
||||||
return err.to_json
|
|
||||||
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.queryUser(params)
|
|
||||||
|
|
||||||
if (gql["user"] == nil)
|
|
||||||
err = {"message" => "Specified user does not exist"}
|
|
||||||
return err.to_json
|
|
||||||
end
|
|
||||||
# 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
|
else
|
||||||
REDIS_DB.set(login.to_s, json_data.to_json, ex: 5)
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return json_data.to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def user(env)
|
def banReason(gql)
|
||||||
# TODO: Clean this mess shit of else if else if else if
|
if gql["userResultByLogin"] == nil
|
||||||
if env.params.query
|
return false
|
||||||
params = env.params.query
|
|
||||||
if params.has_key?("login") || params.has_key?("id")
|
|
||||||
begin
|
|
||||||
parseData(params)
|
|
||||||
rescue ex
|
|
||||||
env.response.status_code = 404
|
|
||||||
err = {"error" => "#{ex.message}"}
|
|
||||||
err.to_json
|
|
||||||
end
|
|
||||||
elsif params.has_key?("id")
|
|
||||||
begin
|
|
||||||
parseData(params)
|
|
||||||
rescue ex
|
|
||||||
env.response.status_code = 404
|
|
||||||
err = {"error" => "#{ex.message}"}
|
|
||||||
err.to_json
|
|
||||||
end
|
|
||||||
else
|
|
||||||
env.response.status_code = 404
|
|
||||||
err = {"error" => "Parameter 'login' or 'id' is missing"}
|
|
||||||
err.to_json
|
|
||||||
end
|
|
||||||
else
|
|
||||||
env.response.status_code = 404
|
|
||||||
err = {"error" => "No query parameters found"}
|
|
||||||
err.to_json
|
|
||||||
end
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
def userLogin(env)
|
||||||
|
if skipCache != "true"
|
||||||
|
if (json = Utils::Redis.retrieveFromCache("user-login_#{env.params.query["login"].try &.split(',')}"))
|
||||||
|
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"]
|
||||||
|
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(',')}"))
|
||||||
|
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|
|
||||||
|
gql = gql["data"]
|
||||||
|
|
||||||
|
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"]
|
||||||
|
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
|
end
|
||||||
|
|
||||||
def modvip(env)
|
def modvip(env)
|
||||||
# if !env.params.query.has_key?("login") && !env.params.query.has_key?("id")
|
channel = env.params.url["channel"]
|
||||||
# err = {"error" => "No query parameters found"}
|
|
||||||
# return err.to_json
|
if (json = Utils::Redis.retrieveFromCache("modvip-#{channel}")) && skipCache != "true"
|
||||||
# else
|
json = JSON.parse(json)
|
||||||
if !env.params.url.has_key?("channel")
|
json.as_h["cache"].as_h["isCached"] = JSON::Any.new(true)
|
||||||
err = {"error" => "Please insert a channel"}
|
json.as_h["cache"].as_h["expireTime"] = JSON::Any.new(Utils::Redis.getExpireTime("modvip-#{channel}"))
|
||||||
return err.to_json
|
return json.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
gql = GqlAPI.getModsVips(env.params.url)["user"]
|
begin
|
||||||
|
gql = GqlAPI.getModsVips(channel)
|
||||||
|
gql = gql["data"]["user"]
|
||||||
|
rescue ex
|
||||||
|
twitchError
|
||||||
|
end
|
||||||
|
|
||||||
if (gql == nil)
|
if (gql == nil)
|
||||||
err = {"message" => "Specified user does not exist"}
|
error("Specified channel does no exist")
|
||||||
return err.to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
json_data = JSON.build do |json|
|
json_data = JSON.build do |json|
|
||||||
json.object do
|
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 "modCount", gql["mods"]["edges"].size
|
||||||
json.field "vipCount", gql["vips"]["edges"].size
|
json.field "vipCount", gql["vips"]["edges"].size
|
||||||
json.field "mods" do
|
json.field "mods" do
|
||||||
|
@ -196,29 +252,43 @@ module TwAPI::Routes::Twitch
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Utils::Redis.saveJson("modvip-#{channel}", json_data)
|
||||||
|
|
||||||
return json_data
|
return json_data
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: Add error message if query doesn't exist at all
|
# TODO: Add error message if query doesn't exist at all
|
||||||
|
|
||||||
def founders(env)
|
def founders(env)
|
||||||
# if !env.params.query.has_key?("login") && !env.params.query.has_key?("id")
|
login = env.params.url["login"]
|
||||||
# err = {"error" => "No query parameters found"}
|
|
||||||
# return err.to_json
|
|
||||||
# else
|
|
||||||
if !env.params.url.has_key?("login")
|
|
||||||
err = {"error" => "Please insert a channel"}
|
|
||||||
return err.to_json
|
|
||||||
end
|
|
||||||
gql = GqlAPI.getFounders(env.params.url)["user"]
|
|
||||||
|
|
||||||
|
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)
|
if (gql == nil)
|
||||||
err = {"message" => "Specified user does not exist"}
|
error("Specified channel does no exist")
|
||||||
return err.to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
json_data = JSON.build do |json|
|
json_data = JSON.build do |json|
|
||||||
json.object do
|
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 "foundersCount", gql["channel"]["founders"].size
|
||||||
json.field "founders" do
|
json.field "founders" do
|
||||||
json.array do
|
json.array do
|
||||||
|
@ -242,27 +312,41 @@ module TwAPI::Routes::Twitch
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Utils::Redis.saveJson("founders-#{login}", json_data)
|
||||||
|
|
||||||
|
return json_data
|
||||||
end
|
end
|
||||||
|
|
||||||
def clip(env)
|
def clip(env)
|
||||||
# if !env.params.query.has_key?("slug")
|
slug = env.params.url["slug"]
|
||||||
# err = {"error" => "No query parameters found"}
|
|
||||||
# return err.to_jso
|
if (json = Utils::Redis.retrieveFromCache("clip-#{slug}")) && skipCache != "true"
|
||||||
# else
|
json = JSON.parse(json)
|
||||||
if !env.params.url.has_key?("slug")
|
json.as_h["cache"].as_h["isCached"] = JSON::Any.new(true)
|
||||||
err = {"error" => "Please insert a slug"}
|
json.as_h["cache"].as_h["expireTime"] = JSON::Any.new(Utils::Redis.getExpireTime("clip-#{slug}"))
|
||||||
return err.to_json
|
return json.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
gql = GqlAPI.getClips(env.params.url)["clip"]
|
begin
|
||||||
|
gql = GqlAPI.getClips(slug)
|
||||||
|
gql = gql["data"]["clip"]
|
||||||
|
rescue ex
|
||||||
|
twitchError
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: use begin rescue for this too! (again)
|
||||||
if (gql == nil)
|
if (gql == nil)
|
||||||
err = {"message" => "Specified user does not exist"}
|
error("Slug does not exist")
|
||||||
return err.to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
json_data = JSON.build do |json|
|
json_data = JSON.build do |json|
|
||||||
json.object do
|
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.field "clip" do
|
||||||
json.object do
|
json.object do
|
||||||
json.field "durationSeconds", gql["durationSeconds"]
|
json.field "durationSeconds", gql["durationSeconds"]
|
||||||
|
@ -321,22 +405,40 @@ module TwAPI::Routes::Twitch
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Utils::Redis.saveJson("clip-#{slug}", json_data)
|
||||||
|
|
||||||
|
return json_data
|
||||||
end
|
end
|
||||||
|
|
||||||
def subage(env)
|
def subage(env)
|
||||||
if !env.params.url.has_key?("user") || !env.params.url.has_key?("channel")
|
user = env.params.url["user"]
|
||||||
err = {"error" => "No query parameters found"}
|
channel = env.params.url["channel"]
|
||||||
return err.to_json
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
gql = GqlAPI.getSubage(env.params.url["user"], env.params.url["channel"])
|
begin
|
||||||
if (gql == nil)
|
gql = GqlAPI.getSubage(user, channel)
|
||||||
err = {"message" => "Specified user does not exist"}
|
gql = gql["data"]
|
||||||
return err.to_json
|
rescue ex
|
||||||
|
if (gql == nil)
|
||||||
|
error("Channel does not exist")
|
||||||
|
end
|
||||||
|
twitchError
|
||||||
end
|
end
|
||||||
|
|
||||||
json_data = JSON.build do |json|
|
json_data = JSON.build do |json|
|
||||||
json.object do
|
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.field "user" do
|
||||||
json.object do
|
json.object do
|
||||||
json.field "id", gql["userData"]["id"]?
|
json.field "id", gql["userData"]["id"]?
|
||||||
|
@ -353,7 +455,7 @@ module TwAPI::Routes::Twitch
|
||||||
end
|
end
|
||||||
json.field "statusHidden", false # TODO: what is this? I need to get it NOW!
|
json.field "statusHidden", false # TODO: what is this? I need to get it NOW!
|
||||||
json.field "followedAt", gql["info"]["relationship"]["followedAt"]
|
json.field "followedAt", gql["info"]["relationship"]["followedAt"]
|
||||||
if (gql["info"]["relationship"]["streak"] == nil)
|
if (gql["info"]["relationship"]["streak"] == nil) || (gql["info"]["relationship"]["streak"]["elapsedDays"]? == 0)
|
||||||
json.field "streak", nil
|
json.field "streak", nil
|
||||||
else
|
else
|
||||||
json.field "streak" do
|
json.field "streak" do
|
||||||
|
@ -366,7 +468,7 @@ module TwAPI::Routes::Twitch
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if (gql["info"]["relationship"]["cumulative"] == nil)
|
if (gql["info"]["relationship"]["cumulative"] == nil) || (gql["info"]["relationship"]["cumulative"]["elapsedDays"]? == 0)
|
||||||
json.field "cumulative", nil
|
json.field "cumulative", nil
|
||||||
else
|
else
|
||||||
json.field "cumulative" do
|
json.field "cumulative" do
|
||||||
|
@ -394,23 +496,39 @@ module TwAPI::Routes::Twitch
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Utils::Redis.saveJson("subage-user_#{user}_channel_#{channel}", json_data)
|
||||||
|
|
||||||
|
return json_data
|
||||||
end
|
end
|
||||||
|
|
||||||
def emotes(env)
|
def emotes(env)
|
||||||
if !env.params.url.has_key?("emote")
|
emoteName = env.params.url["emote"]
|
||||||
err = {"error" => "No query parameters found"}
|
|
||||||
return err.to_json
|
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
|
end
|
||||||
|
|
||||||
gql = GqlAPI.getEmotes(env.params.url["emote"])["emote"] # TODO: support if ID or emote name
|
begin
|
||||||
|
gql = GqlAPI.getEmotes(emoteName)
|
||||||
if (gql == nil)
|
gql = gql["data"]["emote"]
|
||||||
err = {"message" => "Specified emote does not exist"}
|
if gql == nil
|
||||||
return err.to_json
|
raise "Emote does not exist"
|
||||||
|
end
|
||||||
|
rescue ex
|
||||||
|
twitchError
|
||||||
end
|
end
|
||||||
|
|
||||||
json_data = JSON.build do |json|
|
json_data = JSON.build do |json|
|
||||||
json.object do
|
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 "channelName", gql["owner"]["displayName"]
|
||||||
json.field "channelLogin", gql["owner"]["login"]
|
json.field "channelLogin", gql["owner"]["login"]
|
||||||
json.field "channelID", gql["owner"]["id"]
|
json.field "channelID", gql["owner"]["id"]
|
||||||
|
@ -424,20 +542,22 @@ module TwAPI::Routes::Twitch
|
||||||
json.field "emoteTier", gql["subscriptionTier"]
|
json.field "emoteTier", gql["subscriptionTier"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Utils::Redis.saveJson("emotes-#{emoteName}", json_data)
|
||||||
|
|
||||||
|
return json_data
|
||||||
end
|
end
|
||||||
|
|
||||||
def channelEmotes(env)
|
def channelEmotes(env)
|
||||||
if !env.params.query.has_key?("channel")
|
|
||||||
err = {"error" => "No query parameters found"}
|
|
||||||
return err.to_json
|
|
||||||
end
|
|
||||||
gql = GqlAPI.getChannelEmotes(env.params.query["channel"]) # TODO: Add support for IDs an not just the channel name
|
gql = GqlAPI.getChannelEmotes(env.params.query["channel"]) # TODO: Add support for IDs an not just the channel name
|
||||||
if (gql == nil)
|
|
||||||
err = {"message" => "Specified user does not exist"}
|
|
||||||
return err.to_json
|
|
||||||
end
|
|
||||||
json_data = JSON.build do |json|
|
json_data = JSON.build do |json|
|
||||||
json.object do
|
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.field "subProducts" do
|
||||||
json.array do
|
json.array do
|
||||||
gql["user"]["subEmotes"]?.try &.as_a.each do |subEmotes|
|
gql["user"]["subEmotes"]?.try &.as_a.each do |subEmotes|
|
||||||
|
@ -491,14 +611,3 @@ module TwAPI::Routes::Twitch
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# I'll do this later fuck this shit
|
|
||||||
|
|
||||||
# def globalEmotes(env)
|
|
||||||
# if !env.params.query.has_key?("id") || !env.params.query.has_key?("login")
|
|
||||||
# err = "[]"
|
|
||||||
# return err.to_json
|
|
||||||
# else
|
|
||||||
# gql = GqlAPI.getChannelEmotes(env.params.query)
|
|
||||||
# json_data = JSON.build do |json|
|
|
||||||
# end
|
|
52
src/main.cr
52
src/main.cr
|
@ -6,57 +6,21 @@ require "redis"
|
||||||
|
|
||||||
require "./config"
|
require "./config"
|
||||||
require "./routes/**"
|
require "./routes/**"
|
||||||
|
require "./datahandlers/**"
|
||||||
require "./twitchapi/*"
|
require "./twitchapi/*"
|
||||||
|
require "./utils/*"
|
||||||
|
|
||||||
module TwAPI
|
module TwAPI
|
||||||
end
|
end
|
||||||
|
|
||||||
alias TA = TwAPI
|
CONFIG = Config.load
|
||||||
|
|
||||||
CONFIG = Config.load
|
|
||||||
Kemal.config.port = CONFIG.port
|
Kemal.config.port = CONFIG.port
|
||||||
REDIS_DB = Redis::PooledClient.new(unixsocket: CONFIG.redis_socket || nil, url: CONFIG.redis_url || nil)
|
REDIS_UTILS = TwAPI::Utils::Redis
|
||||||
if REDIS_DB.ping
|
REDIS_DB = Redis::Client.new(URI.parse("redis://#{CONFIG.redis_addr}/#{CONFIG.redis_database}#?keepalive=true"))
|
||||||
puts "Connected to redis"
|
puts "Connected to Redis"
|
||||||
end
|
|
||||||
|
|
||||||
before_all "/v2/*" do |env|
|
TwAPI::Routes.register_misc
|
||||||
env.response.content_type = "application/json"
|
TwAPI::Routes.register_all_twitch
|
||||||
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) %}
|
{% if flag?(:release) || flag?(:production) %}
|
||||||
Kemal.config.env = "production" if !ENV.has_key?("KEMAL_ENV")
|
Kemal.config.env = "production" if !ENV.has_key?("KEMAL_ENV")
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
module TwAPI::Routing
|
module TwAPI::Routes
|
||||||
extend self # To prevent writing self. at the start of every function
|
extend self # To prevent writing self. at the start of every function
|
||||||
|
|
||||||
# Thanks Invidious devs
|
# Thanks Invidious devs
|
||||||
{% for http_method in {"get", "post", "delete", "options", "patch", "put"} %}
|
{% for http_method in {"get", "post", "delete", "options", "patch", "put"} %}
|
||||||
|
|
||||||
macro {{http_method.id}}(path, controller, method = :handle)
|
macro {{http_method.id}}(path, controller, method = :handle)
|
||||||
unless Kemal::Utils.path_starts_with_slash?(\{{path}})
|
unless Kemal::Utils.path_starts_with_slash?(\{{path}})
|
||||||
raise Kemal::Exceptions::InvalidPathStartException.new({{http_method}}, \{{path}})
|
raise Kemal::Exceptions::InvalidPathStartException.new({{http_method}}, \{{path}})
|
||||||
|
@ -13,11 +12,23 @@ module TwAPI::Routing
|
||||||
\{{ controller }}.\{{ method.id }}(env)
|
\{{ controller }}.\{{ method.id }}(env)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
def register_all
|
def register_misc
|
||||||
# get "/"
|
before_all "/v2/*" do |env|
|
||||||
|
env.response.content_type = "application/json"
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/" do |env|
|
||||||
|
env.redirect "/v2"
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/v2" do
|
||||||
|
{"message": "Welcome to v2, There is no docs!"}.to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def register_all_twitch
|
||||||
get "/v2/twitch/user", TwAPI::Routes::Twitch, :user
|
get "/v2/twitch/user", TwAPI::Routes::Twitch, :user
|
||||||
|
|
||||||
get "/v2/twitch/modvip/", TwAPI::Routes::Twitch, :modvip
|
get "/v2/twitch/modvip/", TwAPI::Routes::Twitch, :modvip
|
||||||
|
|
52
src/routes/twitch.cr
Normal file
52
src/routes/twitch.cr
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
module TwAPI::Routes::Twitch
|
||||||
|
extend self
|
||||||
|
|
||||||
|
private macro handleData(func)
|
||||||
|
begin
|
||||||
|
TwAPI::Datahandlers::Twitch.{{func}}(env)
|
||||||
|
rescue ex
|
||||||
|
env.response.status_code = 404
|
||||||
|
err = {"error" => "#{ex.message}"}
|
||||||
|
err.to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def user(env)
|
||||||
|
if env.params.query.has_key?("login")
|
||||||
|
handleData(userLogin)
|
||||||
|
else
|
||||||
|
handleData(userId)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def modvip(env)
|
||||||
|
handleData(modvip)
|
||||||
|
end
|
||||||
|
|
||||||
|
def founders(env)
|
||||||
|
handleData(founders)
|
||||||
|
end
|
||||||
|
|
||||||
|
def clip(env)
|
||||||
|
handleData(clip)
|
||||||
|
end
|
||||||
|
|
||||||
|
def subage(env)
|
||||||
|
handleData(subage)
|
||||||
|
end
|
||||||
|
|
||||||
|
def emotes(env)
|
||||||
|
handleData(emotes)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# I'll do this later fuck this shit
|
||||||
|
|
||||||
|
# def globalEmotes(env)
|
||||||
|
# if !env.params.query.has_key?("id") || !env.params.query.has_key?("login")
|
||||||
|
# err = "[]"
|
||||||
|
# return err.to_json
|
||||||
|
# else
|
||||||
|
# gql = GqlAPI.getChannelEmotes(env.params.query)
|
||||||
|
# json_data = JSON.build do |json|
|
||||||
|
# end
|
|
@ -1,18 +1,6 @@
|
||||||
module GqlAPI
|
module TwAPI::GqlAPI
|
||||||
extend self
|
extend self
|
||||||
|
|
||||||
@@headers = HTTP::Headers{
|
|
||||||
"Content-Type" => "application/json",
|
|
||||||
# "Client-Id" => "kimne78kx3ncx6brgo4mv6wki5h1ko", # Can cause problems due to Client-Integrity
|
|
||||||
"Authorization" => "OAuth #{CONFIG.gqlOAuth}",
|
|
||||||
"Client-Id" => "#{CONFIG.gqlClientID}",
|
|
||||||
}
|
|
||||||
@@urb : String = "fuck"
|
|
||||||
|
|
||||||
def urb
|
|
||||||
return @@urb
|
|
||||||
end
|
|
||||||
|
|
||||||
def userResultBy(params)
|
def userResultBy(params)
|
||||||
if params.has_key?("id")
|
if params.has_key?("id")
|
||||||
return %(userResultByID(id: "#{params["id"]}"))
|
return %(userResultByID(id: "#{params["id"]}"))
|
||||||
|
@ -37,27 +25,22 @@ module GqlAPI
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def user(params)
|
def getUserByLogin(params)
|
||||||
if params.has_key?("id")
|
logins = params["login"].try &.split(',')
|
||||||
return %(id: "#{params["id"]}" lookupType: ALL)
|
channel = Channel(JSON::Any).new
|
||||||
elsif params.has_key?("login")
|
responses = [] of JSON::Any
|
||||||
return %(login: "#{params["login"]}" lookupType: ALL)
|
queries = [] of String
|
||||||
else
|
|
||||||
return %(login: "#{params["channel"]}" lookupType: ALL)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def queryUser(params)
|
logins.each do |login|
|
||||||
@@urb = userResultBy(params)
|
query = %(
|
||||||
query = %(
|
|
||||||
query {
|
query {
|
||||||
#{userResultBy(params)} {
|
userResultByLogin(login: "#{login}") {
|
||||||
... on UserDoesNotExist {
|
... on UserDoesNotExist {
|
||||||
key
|
key
|
||||||
reason
|
reason
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
user(#{user(params)}) {
|
user(login: "#{login}" lookupType: ALL) {
|
||||||
id
|
id
|
||||||
language
|
language
|
||||||
login
|
login
|
||||||
|
@ -138,13 +121,140 @@ module GqlAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return (JSON.parse(gqlReq(query)))["data"]
|
queries << query
|
||||||
|
end
|
||||||
|
|
||||||
|
queries.each do |query|
|
||||||
|
spawn do
|
||||||
|
result = JSON.parse(req(query))
|
||||||
|
channel.send(result)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
queries.each do
|
||||||
|
responses << channel.receive
|
||||||
|
end
|
||||||
|
|
||||||
|
return responses
|
||||||
end
|
end
|
||||||
|
|
||||||
def getModsVips(env)
|
def getUserById(params)
|
||||||
|
ids = params["id"].try &.split(',')
|
||||||
|
channel = Channel(JSON::Any).new
|
||||||
|
responses = [] of JSON::Any
|
||||||
|
queries = [] of String
|
||||||
|
|
||||||
|
ids.each do |id|
|
||||||
|
query = %(
|
||||||
|
query {
|
||||||
|
userResultByID(id: "#{id}") {
|
||||||
|
... on UserDoesNotExist {
|
||||||
|
key
|
||||||
|
reason
|
||||||
|
}
|
||||||
|
}
|
||||||
|
user(id: "#{id}" lookupType: ALL) {
|
||||||
|
id
|
||||||
|
language
|
||||||
|
login
|
||||||
|
displayName
|
||||||
|
description
|
||||||
|
bannerImageURL
|
||||||
|
profileImageURL(width: 600)
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
deletedAt
|
||||||
|
chatColor
|
||||||
|
emoticonPrefix {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
panels(hideExtensions: false) {
|
||||||
|
id
|
||||||
|
type
|
||||||
|
}
|
||||||
|
followers {
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
roles {
|
||||||
|
isStaff
|
||||||
|
isAffiliate
|
||||||
|
isPartner
|
||||||
|
isExtensionsDeveloper
|
||||||
|
}
|
||||||
|
displayBadges {
|
||||||
|
setID
|
||||||
|
title
|
||||||
|
description
|
||||||
|
version
|
||||||
|
}
|
||||||
|
chatSettings {
|
||||||
|
autoModLevel
|
||||||
|
blockLinks
|
||||||
|
chatDelayMs
|
||||||
|
followersOnlyDurationMinutes
|
||||||
|
isBroadcasterLanguageModeEnabled
|
||||||
|
isEmoteOnlyModeEnabled
|
||||||
|
isFastSubsModeEnabled
|
||||||
|
isOptedOutOfGlobalBannedWordsList
|
||||||
|
isSubscribersOnlyModeEnabled
|
||||||
|
isUniqueChatModeEnabled
|
||||||
|
requireVerifiedAccount
|
||||||
|
rules
|
||||||
|
slowModeDurationSeconds
|
||||||
|
}
|
||||||
|
stream {
|
||||||
|
averageFPS
|
||||||
|
bitrate
|
||||||
|
codec
|
||||||
|
createdAt
|
||||||
|
width
|
||||||
|
height
|
||||||
|
id
|
||||||
|
viewersCount
|
||||||
|
type
|
||||||
|
game{displayName}
|
||||||
|
}
|
||||||
|
lastBroadcast {
|
||||||
|
game{displayName}
|
||||||
|
id
|
||||||
|
startedAt
|
||||||
|
title
|
||||||
|
}
|
||||||
|
channel {
|
||||||
|
chatters {
|
||||||
|
count
|
||||||
|
moderators {
|
||||||
|
login
|
||||||
|
}
|
||||||
|
vips {
|
||||||
|
login
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
queries << query
|
||||||
|
end
|
||||||
|
|
||||||
|
queries.each do |query|
|
||||||
|
spawn do
|
||||||
|
result = JSON.parse(req(query))
|
||||||
|
channel.send(result)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
queries.each do
|
||||||
|
responses << channel.receive
|
||||||
|
end
|
||||||
|
|
||||||
|
return responses
|
||||||
|
end
|
||||||
|
|
||||||
|
def getModsVips(params)
|
||||||
query = %(
|
query = %(
|
||||||
query {
|
query {
|
||||||
user(#{user(env)}) {
|
user(login: "#{params}" lookupType: ALL) {
|
||||||
mods(first: 100) {
|
mods(first: 100) {
|
||||||
edges {
|
edges {
|
||||||
grantedAt
|
grantedAt
|
||||||
|
@ -169,13 +279,13 @@ module GqlAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return (JSON.parse(gqlReq(query)))["data"]
|
return JSON.parse(req(query))
|
||||||
end
|
end
|
||||||
|
|
||||||
def getFounders(env)
|
def getFounders(params)
|
||||||
query = %(
|
query = %(
|
||||||
query {
|
query {
|
||||||
user(#{user(env)}) {
|
user(login: "#{params}" lookupType: ALL) {
|
||||||
id
|
id
|
||||||
login
|
login
|
||||||
displayName
|
displayName
|
||||||
|
@ -194,13 +304,13 @@ module GqlAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return (JSON.parse(gqlReq(query)))["data"]
|
return JSON.parse(req(query))
|
||||||
end
|
end
|
||||||
|
|
||||||
def getClips(params)
|
def getClips(params)
|
||||||
query = %(
|
query = %(
|
||||||
query {
|
query {
|
||||||
clip(slug: "#{params["slug"]}") {
|
clip(slug: "#{params}") {
|
||||||
createdAt
|
createdAt
|
||||||
creationState
|
creationState
|
||||||
durationSeconds
|
durationSeconds
|
||||||
|
@ -234,14 +344,14 @@ module GqlAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return (JSON.parse(gqlReq(query)))["data"]
|
return JSON.parse(req(query))
|
||||||
end
|
end
|
||||||
|
|
||||||
def getSubage(user, channel)
|
def getSubage(user, channel)
|
||||||
channelId = HelixAPI.getId(channel)
|
channelId = HelixAPI.getId(channel)
|
||||||
query = %(
|
query = %(
|
||||||
query {
|
query {
|
||||||
userData: userResultByLogin (login: "#{user}") {
|
userData: userResultByLogin (login: "#{user}") {
|
||||||
... on UserDoesNotExist { key reason }
|
... on UserDoesNotExist { key reason }
|
||||||
... on UserError { key }
|
... on UserError { key }
|
||||||
... on User { id displayName login }
|
... on User { id displayName login }
|
||||||
|
@ -287,7 +397,7 @@ module GqlAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return (JSON.parse(gqlReq(query)))["data"]
|
return JSON.parse(req(query))
|
||||||
end
|
end
|
||||||
|
|
||||||
def getEmotes(emote)
|
def getEmotes(emote)
|
||||||
|
@ -318,7 +428,7 @@ module GqlAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return (JSON.parse(gqlReq(query)))["data"]
|
return JSON.parse(req(query))
|
||||||
end
|
end
|
||||||
|
|
||||||
def getChannelEmotes(channel)
|
def getChannelEmotes(channel)
|
||||||
|
@ -352,7 +462,7 @@ module GqlAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return (JSON.parse(gqlReq(query)))["data"]
|
return JSON.parse(req(query))
|
||||||
end
|
end
|
||||||
|
|
||||||
def getEmotes(emote)
|
def getEmotes(emote)
|
||||||
|
@ -383,7 +493,7 @@ module GqlAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return (JSON.parse(gqlReq(query)))["data"]
|
return JSON.parse(req(query))
|
||||||
end
|
end
|
||||||
|
|
||||||
def getChannelBadges(channel)
|
def getChannelBadges(channel)
|
||||||
|
@ -410,12 +520,18 @@ module GqlAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return (JSON.parse(gqlReq(query)))["data"]
|
return JSON.parse(req(query))
|
||||||
end
|
end
|
||||||
|
|
||||||
def gqlReq(query)
|
def req(query)
|
||||||
|
headers = HTTP::Headers{
|
||||||
|
"Content-Type" => "application/json",
|
||||||
|
# "Client-Id" => "kimne78kx3ncx6brgo4mv6wki5h1ko", # Can cause problems due to Client-Integrity
|
||||||
|
"Authorization" => "OAuth #{CONFIG.gqlOAuth}",
|
||||||
|
"Client-Id" => "#{CONFIG.gqlClientID}",
|
||||||
|
}
|
||||||
data = {"query" => query}
|
data = {"query" => query}
|
||||||
response = HTTP::Client.post(CONFIG.gqlEndpoint.to_s, headers: @@headers, body: data.to_json)
|
response = HTTP::Client.post(CONFIG.gqlEndpoint.to_s, headers: headers, body: data.to_json)
|
||||||
|
|
||||||
if response.success?
|
if response.success?
|
||||||
return (response.body)
|
return (response.body)
|
|
@ -1,40 +0,0 @@
|
||||||
module HelixAPI
|
|
||||||
extend self
|
|
||||||
|
|
||||||
@@headers = HTTP::Headers{
|
|
||||||
"Content-Type" => "application/json",
|
|
||||||
"Authorization" => "Bearer #{CONFIG.helixOAuth}",
|
|
||||||
"Client-Id" => "#{CONFIG.helixClientID}",
|
|
||||||
}
|
|
||||||
|
|
||||||
def users(params)
|
|
||||||
if params.has_key?("login")
|
|
||||||
endpoint = "#{CONFIG.apiEndpoint}/users?login=#{params["login"]}"
|
|
||||||
else
|
|
||||||
endpoint = "#{CONFIG.apiEndpoint}/users?id=#{params["id"]}"
|
|
||||||
end
|
|
||||||
|
|
||||||
response = HTTP::Client.get(endpoint, headers: @@headers)
|
|
||||||
|
|
||||||
if response.success?
|
|
||||||
return (response.body)
|
|
||||||
else
|
|
||||||
raise "Helix Twitch API returned #{response.status_code}: #{response.body.to_s}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def getId(login)
|
|
||||||
request = "#{CONFIG.apiEndpoint.to_s}/users?login=#{login}"
|
|
||||||
return JSON.parse(helixReq(request))["data"][0]["id"]
|
|
||||||
end
|
|
||||||
|
|
||||||
def helixReq(request)
|
|
||||||
response = HTTP::Client.get(request, headers: @@headers)
|
|
||||||
|
|
||||||
if response.success?
|
|
||||||
return (response.body)
|
|
||||||
else
|
|
||||||
raise "Helix Twitch API returned #{response.status_code}: #{response.body.to_s}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
23
src/twitchapi/helixapi.cr
Normal file
23
src/twitchapi/helixapi.cr
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
module HelixAPI
|
||||||
|
extend self
|
||||||
|
|
||||||
|
def getId(login)
|
||||||
|
request = "#{CONFIG.apiEndpoint.to_s}/users?login=#{login}"
|
||||||
|
return JSON.parse(req(request))["data"][0]["id"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def req(request)
|
||||||
|
headers = HTTP::Headers{
|
||||||
|
"Content-Type" => "application/json",
|
||||||
|
"Authorization" => "Bearer #{CONFIG.helixOAuth}",
|
||||||
|
"Client-Id" => "#{CONFIG.helixClientID}",
|
||||||
|
}
|
||||||
|
response = HTTP::Client.get(request, headers: headers)
|
||||||
|
|
||||||
|
if response.success?
|
||||||
|
return (response.body)
|
||||||
|
else
|
||||||
|
raise "Helix Twitch API returned #{response.status_code}: #{response.body.to_s}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
17
src/utils/redis.cr
Normal file
17
src/utils/redis.cr
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
module TwAPI::Utils::Redis
|
||||||
|
extend self
|
||||||
|
|
||||||
|
def getExpireTime(key)
|
||||||
|
return REDIS_DB.ttl(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
def retrieveFromCache(keyName)
|
||||||
|
if keyName && (json = REDIS_DB.get(keyName))
|
||||||
|
return json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def saveJson(keyName, json_data : String)
|
||||||
|
REDIS_DB.set(keyName, json_data, ex: 60)
|
||||||
|
end
|
||||||
|
end
|
3
src/utils/utils.cr
Normal file
3
src/utils/utils.cr
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module TwAPI::Utils
|
||||||
|
extend self
|
||||||
|
end
|
Loading…
Reference in a new issue