forked from forem/forem
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Embed Twitch Live Streaming (forem#2591)
* Get a job created that can create a webhook subscription for a twitch user login * Remove ngork url * Refactor add store the access token in the cache * Get a controller stood up to recieve the webhooks. Now they just need to be processed * Get User columns added and got webhook controller bones working * Update the webhook job to use the User * Add a way for the User to input their Twitch User Name. Plus a linter fix * Delay webhook registration when profile is updated * Don't add _ in username * Use String columns * Quick fix and add some more requests specs * Specs for the webhook job * Get a show page Twitch Live Streams. Just a straight embed of the Twitch Everything Embed UI. Works surprisingly well responsively, and works on all screen sizes * Fix Gemfile.lock from merge issues * Add support for expired tokens and add spec * Add secrets to webhook registration and clean up spec to remove token logic * Verify webhook secret and spec it * Add rake task to enqueue webhook registration for all Users. This can be used from Heroku Scheduler * Update the lease seconds to be for 5 days * Actually lets do 7 so we can refresh twice a week and try to make sure that we can always miss one * Hijack the existing Twitch logo instead of making a duplicate one * Remove comment and replace with log line * Remove some white space * Log to Airbrake when webhook errors occur * Move to passing in an id instead of User object * Extract logic from Job to Service object * Capitilize in the view * Move out of models and into services * Remove letover stub * Remove one usage of Faraday * Use HTTParty for all the HTTP here
- Loading branch information
1 parent
5b03403
commit ec38880
Showing
24 changed files
with
498 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
class TwitchLiveStreamsController < ApplicationController | ||
before_action :set_cache_control_headers | ||
|
||
def show | ||
@user = User.find_by!(username: params[:username].tr("@", "").downcase) | ||
if @user.twitch_username.present? | ||
render :show | ||
else | ||
render :no_twitch | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
class TwitchStreamUpdatesController < ApplicationController | ||
skip_before_action :verify_authenticity_token | ||
|
||
def show | ||
if params["hub.mode"] == "denied" | ||
airbrake_logger.error("Twitch Webhook was denied: #{params.permit('hub.mode', 'hub.reason', 'hub.topic').to_json}") | ||
head :no_content | ||
else | ||
render plain: params["hub.challenge"] | ||
end | ||
end | ||
|
||
def create | ||
head :no_content | ||
|
||
unless secret_verified? | ||
airbrake_logger.warn("Twitch Webhook Recieved for which the webhook could not be verified") | ||
return | ||
end | ||
|
||
user = User.find(params[:user_id]) | ||
|
||
if params[:data].first.present? | ||
user.update!(currently_streaming_on: :twitch) | ||
else | ||
user.update!(currently_streaming_on: nil) | ||
end | ||
end | ||
|
||
private | ||
|
||
def airbrake_logger | ||
Airbrake::AirbrakeLogger.new(Rails.logger) | ||
end | ||
|
||
def secret_verified? | ||
twitch_sha = request.headers["x-hub-signature"] | ||
digest = Digest::SHA256.new | ||
digest << ApplicationConfig["TWITCH_WEBHOOK_SECRET"] | ||
digest << request.raw_post | ||
|
||
twitch_sha == "sha256=#{digest.hexdigest}" | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module Streams | ||
class TwitchWebhookRegistrationJob < ApplicationJob | ||
queue_as :twitch_webhook_registration | ||
|
||
def perform(user_id, service = TwitchWebhook::Register) | ||
user = User.find_by(id: user_id) | ||
return if user.blank? || user.twitch_username.blank? | ||
|
||
service.call(user) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
module Streams | ||
module TwitchAccessToken | ||
class Get | ||
ACCESS_TOKEN_AND_EXPIRATION_CACHE_KEY = :twitch_access_token_with_expiration | ||
def self.call | ||
new.call | ||
end | ||
|
||
def call | ||
token, exp = Rails.cache.fetch(ACCESS_TOKEN_AND_EXPIRATION_CACHE_KEY) | ||
|
||
if token.nil? || Time.zone.now >= exp | ||
token, exp = get_new_token | ||
Rails.cache.write(ACCESS_TOKEN_AND_EXPIRATION_CACHE_KEY, [token, exp]) | ||
end | ||
|
||
token | ||
end | ||
|
||
private | ||
|
||
def get_new_token | ||
resp = HTTParty.post( | ||
"https://id.twitch.tv/oauth2/token", | ||
body: { | ||
client_id: ApplicationConfig["TWITCH_CLIENT_ID"], | ||
client_secret: ApplicationConfig["TWITCH_CLIENT_SECRET"], | ||
grant_type: :client_credentials | ||
}, | ||
) | ||
[resp["access_token"], resp["expires_in"].seconds.from_now] | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
module Streams | ||
module TwitchWebhook | ||
class Register | ||
WEBHOOK_LEASE_SECONDS = 7.days.to_i | ||
|
||
def initialize(user, access_token_service = TwitchAccessToken::Get) | ||
@user = user | ||
@access_token_service = access_token_service | ||
end | ||
|
||
def self.call(*args) | ||
new(*args).call | ||
end | ||
|
||
def call | ||
user_resp = HTTParty.get("https://api.twitch.tv/helix/users", query: { login: user.twitch_username }, headers: authentication_request_headers) | ||
twitch_user_id = user_resp["data"].first["id"] | ||
|
||
HTTParty.post( | ||
"https://api.twitch.tv/helix/webhooks/hub", | ||
body: webhook_request_body(twitch_user_id), | ||
headers: authentication_request_headers, | ||
) | ||
end | ||
|
||
private | ||
|
||
attr_reader :user, :access_token_service | ||
|
||
def webhook_request_body(twitch_user_id) | ||
{ | ||
"hub.callback" => twitch_stream_updates_url_for_user(user), | ||
"hub.mode" => "subscribe", | ||
"hub.lease_seconds" => WEBHOOK_LEASE_SECONDS, | ||
"hub.topic" => "https://api.twitch.tv/helix/streams?user_id=#{twitch_user_id}", | ||
"hub.secret" => ApplicationConfig["TWITCH_WEBHOOK_SECRET"] | ||
}.to_json | ||
end | ||
|
||
def authentication_request_headers | ||
{ "Authorization" => "Bearer #{access_token_service.call}" } | ||
end | ||
|
||
def twitch_stream_updates_url_for_user(user) | ||
Rails.application.routes.url_helpers.user_twitch_stream_updates_url(user_id: user.id, host: ApplicationConfig["APP_DOMAIN"]) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<div class="home"> | ||
This user does not use Twitch | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<div class="home" id="twitch-embed"> | ||
</div> | ||
|
||
<script id="twitch-embed-js" src="https://embed.twitch.tv/embed/v1.js"></script> | ||
<script type="text/javascript"> | ||
function load_twitch() { | ||
new Twitch.Embed("twitch-embed", { | ||
width: "100%", | ||
height: "600px", | ||
channel: "<%= @user.twitch_username %>" | ||
}); | ||
} | ||
|
||
var scr = document.createElement('script'), | ||
head = document.head || document.getElementsByTagName('head')[0]; | ||
scr.src = 'https://embed.twitch.tv/embed/v1.js'; | ||
scr.onload = load_twitch; | ||
scr.async = true; | ||
|
||
head.insertBefore(scr, head.firstChild); | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
class AddTwitchColumnsToUser < ActiveRecord::Migration[5.2] | ||
def change | ||
change_table :users do |t| | ||
t.string :twitch_username, index: true, unique: true | ||
t.string :currently_streaming_on | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace :twitch do | ||
desc "Register for Webhooks for all User's Registered with Twitch" | ||
task wehbook_register_all: :environment do | ||
User.where.not(twitch_username: nil).find_each do |user| | ||
Streams::TwitchWebhookRegistrationJob.perform_later(user.id) | ||
end | ||
end | ||
end |
Oops, something went wrong.