Skip to content

Commit

Permalink
Merge pull request #106 from true-runes/development
Browse files Browse the repository at this point in the history
v3.0.0
  • Loading branch information
nikukyugamer committed Jun 8, 2021
2 parents d3fdc6b + ef5089b commit c90566b
Show file tree
Hide file tree
Showing 48 changed files with 1,975 additions and 141 deletions.
7 changes: 7 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ POSTGRES_PORT_DEVELOPMENT=5432
POSTGRES_USERNAME_DEVELOPMENT=username
POSTGRES_PASSWORD_DEVELOPMENT=password

POSTGRES_HOST_TEST=
POSTGRES_PORT_TEST=
POSTGRES_USERNAME_TEST=
POSTGRES_PASSWORD_TEST=

TWEET_STORAGE_DATABASE=
TWEET_STORAGE_HOST=
TWEET_STORAGE_PORT=
Expand All @@ -22,3 +27,5 @@ TWITTER_CONSUMER_KEY=
TWITTER_CONSUMER_SECRET=
TWITTER_ACCESS_TOKEN=
TWITTER_ACCESS_SECRET=

LANGUAGE_CREDENTIALS=google_natural_language_api_credentials.json
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ gem 'bootsnap', require: false
gem 'bugsnag'
gem 'dotenv-rails'
gem 'google-apis-sheets_v4'
gem 'google-cloud-language'
gem 'paper_trail'
gem 'pg'
gem 'puma'
Expand All @@ -19,12 +20,12 @@ gem 'twitter'
group :development, :test do
gem 'byebug'
gem 'factory_bot_rails'
gem 'pry-rails'
gem 'rspec-rails'
end

group :development do
gem 'listen'
gem 'pry-rails'
gem 'rubocop-rails'
gem 'spring'
end
Expand Down
34 changes: 34 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@ GEM
ffi-compiler (1.0.1)
ffi (>= 1.0.0)
rake
gapic-common (0.4.1)
faraday (~> 1.3)
google-protobuf (~> 3.15, >= 3.15.2)
googleapis-common-protos (>= 1.3.11, < 2.0)
googleapis-common-protos-types (>= 1.0.6, < 2.0)
googleauth (~> 0.15, >= 0.15.1)
grpc (~> 1.36)
globalid (0.4.2)
activesupport (>= 4.2.0)
google-apis-core (0.3.0)
Expand All @@ -119,13 +126,39 @@ GEM
webrick
google-apis-sheets_v4 (0.6.0)
google-apis-core (~> 0.1)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.5.0)
faraday (>= 0.17.3, < 2.0)
google-cloud-errors (1.1.0)
google-cloud-language (1.3.0)
google-cloud-core (~> 1.5)
google-cloud-language-v1 (~> 0.1)
google-cloud-language-v1beta2 (~> 0.1)
google-cloud-language-v1 (0.4.0)
gapic-common (~> 0.3)
google-cloud-errors (~> 1.0)
google-cloud-language-v1beta2 (0.4.0)
gapic-common (~> 0.3)
google-cloud-errors (~> 1.0)
google-protobuf (3.17.2-x86_64-linux)
googleapis-common-protos (1.3.11)
google-protobuf (~> 3.14)
googleapis-common-protos-types (>= 1.0.6, < 2.0)
grpc (~> 1.27)
googleapis-common-protos-types (1.0.6)
google-protobuf (~> 3.14)
googleauth (0.16.2)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (~> 0.14)
grpc (1.38.0-x86_64-linux)
google-protobuf (~> 3.15)
googleapis-common-protos-types (~> 1.0)
http (4.4.1)
addressable (~> 2.3)
http-cookie (~> 1.0)
Expand Down Expand Up @@ -314,6 +347,7 @@ DEPENDENCIES
dotenv-rails
factory_bot_rails
google-apis-sheets_v4
google-cloud-language
listen
paper_trail
pg
Expand Down
49 changes: 49 additions & 0 deletions app/lib/google_natural_language_api/analyzer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# https://googleapis.dev/ruby/google-cloud-language/latest/file.MIGRATING.html
# https://googleapis.dev/ruby/google-cloud-language-v1/latest/Google/Cloud/Language/V1/AnalyzeSyntaxResponse.html
# https://cloud.google.com/natural-language/#natural-language-api-demo
# GoogleNaturalLanguageApi::PickupCharacterNames.new.foobar

require "google/cloud/language"

module GoogleNaturalLanguageApi
class Analyzer
attr_reader :client

def self.execute_and_create_record_to_database
analyzer = new

ActiveRecord::Base.transaction do
Tweet.not_retweet.each do |tweet|
analyzer.create_analyze_syntax_record(tweet) if AnalyzeSyntax.find_by(tweet_id: tweet.id).blank?
end
end
end

def initialize
@language = Google::Cloud::Language.language_service
@client = Google::Cloud::Language.language_service
end

def create_analyze_syntax_record(tweet)
response = analyze_tweet_syntax_by_api(tweet)

ActiveRecord::Base.transaction do
analyze_syntax = AnalyzeSyntax.new(
language: response.language,
sentences: response.sentences.map(&:to_json),
tokens: response.tokens.map(&:to_json),
tweet_id: tweet.id
)

analyze_syntax.save!
end
end

def analyze_tweet_syntax_by_api(tweet)
content = tweet.full_text
document = { type: :PLAIN_TEXT, content: content }

@language.analyze_syntax(document: document)
end
end
end
130 changes: 129 additions & 1 deletion app/lib/importer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def tweet_records_from_tweet_storage(ts_target_tweets, _update: false)
screen_name: ts_target_tweet.user.screen_name,
profile_image_url_https: ts_target_tweet.user.deserialize.profile_image_url_https.to_s,
is_protected: false,
born_at: ts_target_tweet.created_at
born_at: ts_target_tweet.user.created_at
}

user = User.find_by(id_number: user_attrs[:id_number])
Expand Down Expand Up @@ -138,5 +138,133 @@ def tweet_records_from_tweet_storage(ts_target_tweets, _update: false)
def direct_message_records_from_tweet_storage
# TODO: DirectMessage の場合を書く
end

# rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
def tweet_records_from_api_tweet_objects(api_tweet_objects, _update: false)
ActiveRecord::Base.transaction do
api_tweet_objects.each do |api_tweet|
###############
# User
###############
user_attrs = {
id_number: api_tweet.user.id,
name: api_tweet.user.name.instance_of?(Twitter::NullObject) ? 'Twitter::NullObject' : api_tweet.user.name,
screen_name: api_tweet.user.screen_name,
profile_image_url_https: api_tweet.user.profile_image_url_https.to_s,
is_protected: false,
born_at: api_tweet.user.created_at
}

user = User.find_by(id_number: user_attrs[:id_number])
if user.blank?
new_user = User.new(user_attrs)
new_user.save!
end

###############
# Tweet
###############
tweet_attrs = {
id_number: api_tweet.id,
full_text: api_tweet.full_text,
source: api_tweet.source,
in_reply_to_tweet_id_number: api_tweet.in_reply_to_status_id,
in_reply_to_user_id_number: api_tweet.in_reply_to_user_id,
is_retweet: api_tweet.retweet?,
language: api_tweet.lang,
is_public: true,
tweeted_at: api_tweet.created_at,
user: new_user || user
}

tweet = Tweet.find_by(id_number: tweet_attrs[:id_number])

if tweet.blank?
new_tweet = Tweet.new(tweet_attrs)
new_tweet.save!
else
next
end

###############
# Asset
###############
api_tweet.media.each do |asset|
asset_attrs = {
id_number: asset.id,
url: asset.media_url_https,
asset_type: asset.type,
is_public: true,
tweet: new_tweet || tweet
}

asset = Asset.find_by(id_number: asset_attrs[:id_number])
if asset.blank?
new_asset = Asset.new(asset_attrs)
new_asset.save!
end
end

###############
# Hashtag
###############
api_tweet.hashtags.each do |hashtag|
hashtag_attrs = {
text: hashtag.text,
tweet: new_tweet || tweet
}

hashtag = Hashtag.find_by(
text: hashtag_attrs[:text],
tweet_id: (new_tweet || tweet).id
)
if hashtag.blank?
new_hashtag = Hashtag.new(hashtag_attrs)
new_hashtag.save!
end
end

###############
# InTweetUrl
###############
# ツイートの URL ではなく、ツイートに含まれている URL
api_tweet.urls.each do |url|
in_tweet_url_attrs = {
text: url.expanded_url.to_s,
tweet: new_tweet || tweet
}

in_tweet_url = InTweetUrl.find_by(
text: in_tweet_url_attrs[:text],
tweet_id: (new_tweet || tweet).id
)
if in_tweet_url.blank?
new_in_tweet_url = InTweetUrl.new(in_tweet_url_attrs)
new_in_tweet_url.save!
end
end

###############
# Mention
###############
api_tweet.user_mentions.each do |mention|
mention_attrs = {
user_id_number: mention.id,
tweet: new_tweet || tweet
}

mention = Mention.find_by(
user_id_number: mention_attrs[:user_id_number],
tweet_id: (new_tweet || tweet).id
)
if mention.blank?
new_mention = Mention.new(mention_attrs)
new_mention.save!
end
end
end
end
end
# rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
end
end
93 changes: 93 additions & 0 deletions app/lib/twitter_rest_api/get_mentions_for_me.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# REST API の認証ユーザに対しての mention を収集する
module TwitterRestApi
class GetMentionsForMe
include LoggerMethods

attr_reader :client

INTERVAL_SECONDS = 5

def initialize(consumer_key: nil, consumer_secret: nil, access_token: nil, access_secret: nil)
@client = Twitter::REST::Client.new do |config|
config.consumer_key = consumer_key || ENV['TWITTER_CONSUMER_KEY']
config.consumer_secret = consumer_secret || ENV['TWITTER_CONSUMER_SECRET']
config.access_token = access_token || ENV['TWITTER_ACCESS_TOKEN']
config.access_token_secret = access_secret || ENV['TWITTER_ACCESS_SECRET']
end
end

# max_id「〜以下」, since_id「〜より大きい」
# 以下のロジックは mentions_timeline に限らず一般的な search の戻り値全てに用いることができる
def execute(initial_execution: false)
all_mention_tweets = []

# 全取得の場合は tweet の id が大きい方から小さい方へ取得される
if initial_execution == true
# 1周目
mention_tweets = @client.mentions_timeline(
{
tweet_mode: 'extended',
count: 200
}
)
all_mention_tweets += mention_tweets
min_tweet_id_number_in_mention_tweets_set = mention_tweets.map(&:id).min

# 2周目以降、取得ツイートが尽きるまで
# rubocop:disable Style/InfiniteLoop
while true
break if min_tweet_id_number_in_mention_tweets_set.blank?

mention_tweets = @client.mentions_timeline(
{
tweet_mode: 'extended',
max_id: min_tweet_id_number_in_mention_tweets_set - 1,
count: 200
}
)

break if mention_tweets.blank?

all_mention_tweets += mention_tweets
min_tweet_id_number_in_mention_tweets_set = mention_tweets.map(&:id).min
end
# rubocop:enable Style/InfiniteLoop
else
# 1周目
max_tweet_id_number_in_already_getting_tweets = Tweet.not_retweet.mentioned_user(User.find_by(id_number: @client.user.id)).order(id_number: :desc).first.id_number

mention_tweets = @client.mentions_timeline(
{
tweet_mode: 'extended',
since_id: max_tweet_id_number_in_already_getting_tweets,
count: 200
}
)
all_mention_tweets += mention_tweets
max_tweet_id_number_in_mention_tweets = mention_tweets.map(&:id).max

# 2周目以降、取得ツイートが尽きるまで
# rubocop:disable Style/InfiniteLoop
while true
break if max_tweet_id_number_in_mention_tweets.blank?

mention_tweets = @client.mentions_timeline(
{
tweet_mode: 'extended',
since_id: max_tweet_id_number_in_mention_tweets,
count: 200
}
)

break if mention_tweets.blank?

all_mention_tweets += mention_tweets
max_tweet_id_number_in_mention_tweets = mention_tweets.map(&:id).max
end
# rubocop:enable Style/InfiniteLoop
end

all_mention_tweets
end
end
end
Loading

0 comments on commit c90566b

Please sign in to comment.