From ecfc4259aa40e29961b737233339959b6d14cdc7 Mon Sep 17 00:00:00 2001 From: Osamu Takiya Date: Thu, 23 Jun 2022 09:11:11 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20darwin=20=E7=94=A8?= =?UTF-8?q?=E3=81=AE=20Gemfile.lock=20=E3=81=AE=E8=A8=98=E8=BF=B0=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0=E3=81=97=E3=81=9F=20(#133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile.lock | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index be21e234..d4dc7f4c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -170,6 +170,7 @@ GEM gapic-common (>= 0.7, < 2.a) google-cloud-errors (~> 1.0) google-protobuf (3.21.1) + google-protobuf (3.21.1-x86_64-darwin) google-protobuf (3.21.1-x86_64-linux) googleapis-common-protos (1.3.12) google-protobuf (~> 3.14) @@ -187,6 +188,9 @@ GEM grpc (1.46.3) google-protobuf (~> 3.19) googleapis-common-protos-types (~> 1.0) + grpc (1.46.3-x86_64-darwin) + google-protobuf (~> 3.19) + googleapis-common-protos-types (~> 1.0) grpc (1.46.3-x86_64-linux) google-protobuf (~> 3.19) googleapis-common-protos-types (~> 1.0) @@ -244,6 +248,8 @@ GEM nio4r (2.5.8) nokogiri (1.13.6-aarch64-linux) racc (~> 1.4) + nokogiri (1.13.6-x86_64-darwin) + racc (~> 1.4) nokogiri (1.13.6-x86_64-linux) racc (~> 1.4) os (1.1.4) @@ -373,6 +379,7 @@ GEM PLATFORMS aarch64-linux + x86_64-darwin-21 x86_64-linux DEPENDENCIES From d42935eb1bb326411e7839168dc49a04d655583b Mon Sep 17 00:00:00 2001 From: Osamu Takiya Date: Thu, 23 Jun 2022 09:13:57 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20=E4=B8=8D=E5=8F=AF?= =?UTF-8?q?=E8=A6=96=E3=81=AB=E3=81=AA=E3=81=A3=E3=81=9F=E3=83=84=E3=82=A4?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=81=AE=E7=8A=B6=E6=85=8B=E3=82=92=E5=A4=89?= =?UTF-8?q?=E6=9B=B4=E3=81=99=E3=82=8B=E3=82=BF=E3=82=B9=E3=82=AF=E3=82=92?= =?UTF-8?q?=E4=BD=9C=E6=88=90=E3=81=97=E3=81=9F=20(#134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ..._visible_and_invisible_tweet_id_numbers.rb | 27 +++++++++++++++++++ lib/tasks/change_is_public_attributes.rake | 27 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 app/lib/check_visible_and_invisible_tweet_id_numbers.rb create mode 100644 lib/tasks/change_is_public_attributes.rake diff --git a/app/lib/check_visible_and_invisible_tweet_id_numbers.rb b/app/lib/check_visible_and_invisible_tweet_id_numbers.rb new file mode 100644 index 00000000..111788d5 --- /dev/null +++ b/app/lib/check_visible_and_invisible_tweet_id_numbers.rb @@ -0,0 +1,27 @@ +class CheckVisibleAndInvisibleTweetIdNumbers + INTERVAL_SECONDS = 5 + + def self.exec(tweet_id_numbers, client_account_key: :ayy) + client = TwitterRestApi.client(account_key: client_account_key) + + all_invisible_tweet_id_numbers = [] + + # 不可視の場合には client.statuses([tweet_id_number, ...]) の戻り値に返ってこない仕様を利用する + tweet_id_numbers.each_slice(100) do |this_tweet_id_numbers| + visible_tweet_objects = client.statuses(this_tweet_id_numbers) # API が消費される + visible_tweet_id_numbers = visible_tweet_objects.map(&:id) # #id により id_number を取得できる + + # 配列 から 配列 を引き算して、不可視のツイートの id_number を取り出す + invisible_tweet_id_numbers = this_tweet_id_numbers - visible_tweet_id_numbers + + all_invisible_tweet_id_numbers << invisible_tweet_id_numbers + all_invisible_tweet_id_numbers.flatten! + + sleep INTERVAL_SECONDS + end + + all_visible_tweet_id_numbers = Tweet.all.map(&:id_number) - all_invisible_tweet_id_numbers + + { visible: all_visible_tweet_id_numbers, invisible: all_invisible_tweet_id_numbers} + end +end diff --git a/lib/tasks/change_is_public_attributes.rake b/lib/tasks/change_is_public_attributes.rake new file mode 100644 index 00000000..a8f109f4 --- /dev/null +++ b/lib/tasks/change_is_public_attributes.rake @@ -0,0 +1,27 @@ +namespace :change_is_public_attributes do + desc 'ツイートが非公開または削除になっていないかを調べ、変更があれば UPDATE を実行する' + task all: :environment do + tweets = Tweet.all + tweet_id_numbers = tweets.map(&:id_number) + + # app/lib/check_visible_and_invisible_tweet_id_numbers.rb を参照のこと + visible_and_invisible_tweet_id_numbers = CheckVisibleAndInvisibleTweetIdNumbers.exec(tweet_id_numbers) + + visible_tweet_id_numbers = visible_and_invisible_tweet_id_numbers[:visible] + invisible_tweet_id_numbers = visible_and_invisible_tweet_id_numbers[:invisible] + + ActiveRecord::Base.transaction do + # 鍵ツイート => 公開ツイート の場合 + Tweet.where(id_number: visible_tweet_id_numbers).each do |tweet| + tweet.update!(is_public: true) unless tweet.public? + end + + # 公開ツイート => 鍵ツイート or 削除ツイート の場合 + Tweet.where(id_number: invisible_tweet_id_numbers).each do |tweet| + tweet.update!(is_public: false) if tweet.public? + end + end + + puts '[DONE] check_tweet_is_invisible:exec' + end +end From 439d0b1075c5b5911cefed614b6c5d62a05cd5c4 Mon Sep 17 00:00:00 2001 From: Osamu Takiya Date: Thu, 23 Jun 2022 09:18:47 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Tweet=20or=20DirectM?= =?UTF-8?q?essage=20=E3=81=AE=E3=82=AA=E3=83=96=E3=82=B8=E3=82=A7=E3=82=AF?= =?UTF-8?q?=E3=83=88=E3=82=92=E6=B8=A1=E3=81=97=E3=81=9F=E3=82=89=E5=80=99?= =?UTF-8?q?=E8=A3=9C=E3=82=AD=E3=83=A3=E3=83=A9=E5=90=8D=E3=81=8C=E8=BF=94?= =?UTF-8?q?=E3=82=8B=E3=82=B5=E3=83=BC=E3=83=93=E3=82=B9=E3=82=AF=E3=83=A9?= =?UTF-8?q?=E3=82=B9=E3=82=92=E4=BD=9C=E6=88=90=20(#135)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/services/natural_language/suggest_character_names.rb | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 app/services/natural_language/suggest_character_names.rb diff --git a/app/services/natural_language/suggest_character_names.rb b/app/services/natural_language/suggest_character_names.rb new file mode 100644 index 00000000..96684d24 --- /dev/null +++ b/app/services/natural_language/suggest_character_names.rb @@ -0,0 +1,9 @@ +module NaturalLanguage + class SuggestCharacterNames + def self.exec(tweet_or_dm) + analyze_syntax = tweet_or_dm.analyze_syntax + + PickupCharacterNames.exec(analyze_syntax) + end + end +end From f64d930333eaad77f50ff441c7ad74f8feb375bf Mon Sep 17 00:00:00 2001 From: Osamu Takiya Date: Thu, 23 Jun 2022 10:40:06 +0900 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20=E6=8A=95=E7=A5=A8?= =?UTF-8?q?=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E7=94=A8=E3=81=AE=20API=20?= =?UTF-8?q?=E3=82=92=E4=BD=9C=E6=88=90=E3=81=97=E3=81=9F=20(#137)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/check_vote_controller.rb | 16 ---- .../check_votes_and_bonuses_controller.rb | 21 ++++++ app/lib/presenter.rb | 5 ++ app/models/tweet.rb | 75 +++++++++++++++---- .../index.json.jbuilder | 5 ++ config/routes.rb | 2 +- ...check_votes_and_bonuses_controller_spec.rb | 31 ++++++++ 7 files changed, 125 insertions(+), 30 deletions(-) delete mode 100644 app/controllers/check_vote_controller.rb create mode 100644 app/controllers/check_votes_and_bonuses_controller.rb create mode 100644 app/views/check_votes_and_bonuses/index.json.jbuilder create mode 100644 spec/requests/check_votes_and_bonuses_controller_spec.rb diff --git a/app/controllers/check_vote_controller.rb b/app/controllers/check_vote_controller.rb deleted file mode 100644 index 371194bb..00000000 --- a/app/controllers/check_vote_controller.rb +++ /dev/null @@ -1,16 +0,0 @@ -class CheckVoteController < ApplicationController - def index - # TODO: @ の取り除き処理など - screen_name = params[:screen_name] || 'gensosenkyo' - - # TODO: ハッシュタグ用のテーブルを作るのでそこから引っ張ってくる - # user = User.find_by(screen_name: screen_name) - # general_votes = user.general_votes - # unite_attack_votes = user.unite_attack_votes - # { general_votes: general_votes, unite_attack_votes: unite_attack_votes } - # アソシエーション用の不要なカラムは返さなくていい - # 返すのは、screen_name、tweet_id_number、(URL)あたり - - render json: { status: "This is a check vote API. Are you #{screen_name}?" } - end -end diff --git a/app/controllers/check_votes_and_bonuses_controller.rb b/app/controllers/check_votes_and_bonuses_controller.rb new file mode 100644 index 00000000..9268d6fc --- /dev/null +++ b/app/controllers/check_votes_and_bonuses_controller.rb @@ -0,0 +1,21 @@ +class CheckVotesAndBonusesController < ApplicationController + def index + screen_name = Presenter::Common.normalized_screen_name(params[:screen_name]) + user = User.find_by(screen_name: screen_name) + + # TODO: エラーハンドリング + return render json: {} if user.blank? + + gss2022_tweets = user.tweets.gensosenkyo_2022_votes_for_api + unite_attacks_tweets = user.tweets.unite_attacks_votes_for_api + short_stories = user.tweets.short_stories + fav_quotes = user.tweets.fav_quotes + sosenkyo_campaigns = user.tweets.sosenkyo_campaigns + + @gss2022_tweets = gss2022_tweets.map(&:id_number) + @unite_attacks_tweets = unite_attacks_tweets.map(&:id_number) + @short_stories = short_stories.map(&:id_number) + @fav_quotes = fav_quotes.map(&:id_number) + @sosenkyo_campaigns = sosenkyo_campaigns.map(&:id_number) + end +end diff --git a/app/lib/presenter.rb b/app/lib/presenter.rb index 8e8b6714..70cb1e56 100644 --- a/app/lib/presenter.rb +++ b/app/lib/presenter.rb @@ -31,5 +31,10 @@ def self.japanese_clock_time_strftime(time, with_seconds: true) time.strftime(str) end + + def self.normalized_screen_name(screen_name) + screen_name.gsub!(' ', '') + screen_name.gsub('@', '') + end end end diff --git a/app/models/tweet.rb b/app/models/tweet.rb index 859a339c..6b06f159 100644 --- a/app/models/tweet.rb +++ b/app/models/tweet.rb @@ -21,15 +21,6 @@ def self.filter_by_tweeted_at(from, to) where(tweeted_at: from..to) end - def self.gensosenkyo_2022_votes - valid_term_votes - .not_retweet - .contains_hashtag('幻水総選挙2022') - .not_by_gensosenkyo_main - .order(tweeted_at: :asc) - .order(id_number: :asc) - end - def self.not_by_gensosenkyo_family # gensosenkyo: 1471724029, # sub_gensosenkyo: 1388758231825018881 @@ -65,20 +56,78 @@ def self.valid_term_votes where(tweeted_at: begin_datetime..end_datetime) end - def self.short_stories + # スプレッドシートに書き込むことを前提としているので、制限は緩い + def self.gensosenkyo_2022_votes + not_retweet # 元データに入っていないから、おそらく不要 + .contains_hashtag('幻水総選挙2022') + .not_by_gensosenkyo_family # 元データに入っていないから、おそらく不要 + .order(tweeted_at: :asc) # id_number で並べているから、おそらく不要 + .order(id_number: :asc) + end + + # API では "is_public" と 期間 に注意する + def self.gensosenkyo_2022_votes_for_api + where(is_public: true) + .valid_term_votes + .not_retweet + .not_by_gensosenkyo_family + .contains_hashtag('幻水総選挙2022') + .order(tweeted_at: :asc) + .order(id_number: :asc) + end + + # スプレッドシートに書き込むことを前提としているので、制限は緩い + def self.unite_attacks_votes not_retweet .not_by_gensosenkyo_family + .contains_hashtag('幻水総選挙2022協力攻撃') + .order(tweeted_at: :asc) + .order(id_number: :asc) + end + + # API では "is_public" と 期間 に注意する + def self.unite_attacks_votes_for_api + where(is_public: true) + .valid_term_votes + .not_retweet + .not_by_gensosenkyo_family + .contains_hashtag('幻水総選挙2022協力攻撃') + .order(tweeted_at: :asc) + .order(id_number: :asc) + end + + # スプレッドシートに書き込むことを前提としているので、制限は緩い + def self.short_stories + not_retweet .contains_hashtag('幻水総選挙お題小説') - .where(tweeted_at: ..Time.zone.parse('2022-06-26 23:59:59')) + .not_by_gensosenkyo_family + .where( + tweeted_at: Time.zone.parse('2022-05-01 12:00:00')..Time.zone.parse('2022-07-31 23:59:59') + ) .order(tweeted_at: :asc) .order(id_number: :asc) end + # スプレッドシートに書き込むことを前提としているので、制限は緩い def self.fav_quotes not_retweet - .not_by_gensosenkyo_family .contains_hashtag('幻水総選挙推し台詞') - .where(tweeted_at: ..Time.zone.parse('2022-06-26 23:59:59')) + .not_by_gensosenkyo_family + .where( + tweeted_at: Time.zone.parse('2022-05-01 12:00:00')..Time.zone.parse('2022-07-31 23:59:59') + ) + .order(tweeted_at: :asc) + .order(id_number: :asc) + end + + # スプレッドシートに書き込むことを前提としているので、制限は緩い + def self.sosenkyo_campaigns + not_retweet + .contains_hashtag('幻水総選挙運動') + .not_by_gensosenkyo_family + .where( + tweeted_at: Time.zone.parse('2022-05-01 12:00:00')..Time.zone.parse('2022-07-31 23:59:59') + ) .order(tweeted_at: :asc) .order(id_number: :asc) end diff --git a/app/views/check_votes_and_bonuses/index.json.jbuilder b/app/views/check_votes_and_bonuses/index.json.jbuilder new file mode 100644 index 00000000..61e6ba74 --- /dev/null +++ b/app/views/check_votes_and_bonuses/index.json.jbuilder @@ -0,0 +1,5 @@ +json.set! 'gss2022', @gss2022_tweets +json.set! 'unite_attacks', @unite_attacks_tweets +json.set! 'short_stories', @short_stories +json.set! 'fav_quotes', @fav_quotes +json.set! 'sosenkyo_campaigns', @sosenkyo_campaigns diff --git a/config/routes.rb b/config/routes.rb index 6c9a0e70..955c3631 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,5 +2,5 @@ get 'health_check', to: 'health_check#index', format: 'json' get 'unite_attacks', to: 'unite_attacks#index', format: 'json' get 'result_illustration_applications', to: 'result_illustration_applications#index', format: 'json' - get 'check_vote', to: 'check_vote#index', format: 'json' + get 'check_votes_and_bonuses', to: 'check_votes_and_bonuses#index', format: 'json' end diff --git a/spec/requests/check_votes_and_bonuses_controller_spec.rb b/spec/requests/check_votes_and_bonuses_controller_spec.rb new file mode 100644 index 00000000..7540d0a7 --- /dev/null +++ b/spec/requests/check_votes_and_bonuses_controller_spec.rb @@ -0,0 +1,31 @@ +require 'rails_helper' + +RSpec.describe CheckVotesAndBonusesController, type: :request do + describe '#index' do + context 'パラメータ screen_name が' do + it 'データベースに存在しないとき、期待通りのレスポンスが返ってくること' do + get check_votes_and_bonuses_path(screen_name: '@test_user') + + expect(response).to have_http_status :ok + expect(response.body).to eq '{}' + end + + it 'データベースに存在するとき、期待通りのレスポンスが返ってくること' do + create(:user) + + get check_votes_and_bonuses_path(screen_name: '@test_screen_name') + + expect(response).to have_http_status :ok + expect(JSON.parse(response.body)).to eq( + { + "gss2022" => [], + "unite_attacks" => [], + "short_stories" => [], + "fav_quotes" => [], + "sosenkyo_campaigns" => [] + } + ) + end + end + end +end